I'm facing a apparently very strange problem (I must be doing something wrong, just can't find my errors!). When certain POCOs are saved into the database, nothing happens. When the same POCOs have some property changed, I get a InvalidCastException during session flush, and the rows are never updated. Here's the details:
I have the following class declared:
namespace Data
{
public class Picture
{
public virtual int picid { get; set; }
public virtual int width { get; set; }
public virtual int height { get; set; }
public virtual string path { get; set; }
public virtual string thumbnail { get; set; }
public virtual int userid { get; set; }
public virtual int? placeid { get; set; }
public virtual int? eventid { get; set; }
public virtual DateTime? approved { get; set; }
public virtual DateTime date { get; set; }
public virtual bool finished { get; set; }
public virtual User User { get; set; }
public virtual Place Place { get; set; }
public virtual Event Event { get; set; }
public virtual ISet<PictureVote> Votes { get; set; }
}
}
and the following mapping for it:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="Data" namespace="Data">
<class name="Picture" table="pictures">
<id name="picid">
<generator class="sequence">
<param name="sequence">pictures_picid_seq</param>
</generator>
</id>
<property name="path" />
<property name="width" />
<property name="height" />
<property name="thumbnail" />
<property name="userid" />
<property name="placeid" not-null="false" />
<property name="eventid" not-null="false" />
<property name="approved" />
<property name="date" />
<property name="finished" />
<many-to-one name="User" column="userid" class="Data.User,Data" insert="false" />
<many-to-one name="Place" column="placeid" class="Data.Place,Data" insert="false" />
<many-to-one name="Event" column="eventid" class="Data.Event,Data" insert="false" />
<set name="Votes">
<key column="picid" />
<one-to-many class="PictureVote" />
</set>
</class>
</hibernate-mapping>
I checked the table definition and all the types seem to be according to the defined class (postgresql):
Table "public.pictures"
Column | Type | Modifiers
-----------+-----------------------------+----------------------------------------------------------
picid | integer | not null default nextval('pictures_picid_seq'::regclass)
path | character varying(250) | not null
thumbnail | character varying(250) | not null
userid | integer | not null
placeid | integer |
date | timestamp without time zone | not null
finished | boolean | not null default false
width | integer | not null
height | integer | not null
eventid | integer |
approved | timestamp without time zone |
When inside the code, the following works just fine and inserts a row in the pictures table:
...
...
var plpic = new Picture
{
date = DateTime.Now,
width = img.Width,
height = img.Height,
path = pic_server_path,
thumbnail = thumb_server_path,
userid = CustomUserManagement.User.userid,
finished = false,
approved = null,
placeid = placeid
};
session.Save(plpic);
session.Flush ();
...
...
which works ok every time (and yes, I'm going to wrap it in a transaction soon enough).
However, later on, the following NEVER works (this is the code inside a MVC action):
...
...
Picture pic = session.Get<Picture>(picid);
// While debugging, I verified that the "pic" object above is retrieved just fine.
if (pic.userid != CustomUserManagement.User.userid || ModelState.IsValid == false)
return Json (new { status = "error" });
using (ITransaction tx = session.BeginTransaction()) {
try
{
pic.finished = true;
tx.Commit();
}
catch (Exception e) {
tx.Rollback();
NHibernateHelper.DestroySession();
return Json (new { status = "error" });
}
}
...
...
But this always throws a System.InvalidCastException: Cannot cast from source type to destination type at NpgsqlTypes.NpgsqlTypesHelper+c_Iterator11.<>m_C (System.Object timestamp) [0x00000] in /Users/fxjr/Desenvolvimento/ProjetosOpenSource/Npgsql/NpgsqlSourceRelease/Npgsql2/src/NpgsqlTypes/NpgsqlTypesHelper.cs:608
What am I doing wrong? I'm using .NET 4 in Mono 2.10.5, even though the same happens on Windows, NHibernate 3.2 and Npgsql 2.0.11.91. I'm also using postgresql 9.1.1, but I have set ansi_conforming_strings to OFF to make sure my Nhibernate dialect still works. For more information, the same thing happens when updating other types of objects.
I have already posted this in the nhusers list and received a very good suggestion on trying a different db to check if it its Npgsql's fault. However, I don't have the time for that right now and since I'm beginning to think it is my fault and not someone else's, I thought I'd post this here before recreating my db schema in another database and trying this code in it. I'm getting a little desperate as time is kind of running out on me.. can anyone save me on this one?
Thanks in advance.
After a long time, I thought I should come back here to answer my own question.
This is not necessarily a bug in either NHibernate or Npgsql.
I was making a huge mistake in the mapping files, by mapping the same column twice. What this means is that having a int? eventid and a Event Event in my mapped class both map to same column will create you problems, since NHibernate can't deal with this, i.e. you have to pick one of them. What happens is that NHibernate will generate queries with more parameters than the actual number of columns in your table. In MS SQL Server, it is easier to understand the thrown exception:
Invalid index N for this SqlParameterCollection with Count=N
Another alternative is mapping both but setting insert="false" and update="false" in one of them, although this is hardly useful.
I think a lot of people like me (coming from LINQ-TO-SQL) will try the same feat and have a lot of trouble later on. Sadly, I haven't found anything that emphasizes this in NHibernate's documentation.
Hope it helps someone.
Related
I want to get ClassA.ClassBCollection property filtered and paged. I need to change filtering dynamically.
The default querying will result in something like:
select * from ClassA
left outer join ClassB
on id == FK_ClassB
Can I customize querying of nhibernate set somehow?
Mappings:
<class name="ClassA">
<property name="Name" />
<set name="ClassBCollection">
<key column="FK_ClassB" on-delete="cascade" />
<one-to-many class="ClassB" />
</set>
</class>
<class name="ClassB">
<property name="Something"/>
</class>
If I do understand your question...
Can I customize querying of nhibernate set somehow?
...correctly, the answer is NO.
I mean, if you think about getting the instance of ClassA and doing some paging and filtering over its <set> collection. That would be always done in memory. (What we can do with mapping I appended at the end).
we can change the approach
In this case, when you need a filter and paging over the collection items, I would strongly recommend to go the other way. Create Criteria (QueryOver, HQL) not over the ClassA but over the ClassB.
First of all we have to extend ClassB mapping:
<class name="ClassB">
<property name="Something" />
<many-to-one name="ClassA" column="FK_ClassB" fetch="join" />
</class>
And then create a Criteria like this
var criteria = NHSession.Current.CreateCriteria<ClassB>();
criteria
.Add(new InExpression("ClassA", new object[] {1})) // id of one or more ClassA
.AddOrder(new Order("Something", true)) // Order By
.SetFirstResult(2) // Skip
.SetMaxResults(10); // Take
var list = criteria.List<ClassB>();
Because we used mapping of ClassA fetch="join" the resulting SQL statement will be very similar to the first snippet in this question.
So this way, we can achieve the desired SQL Select, but we cannot use ClassA.ClassBCollection directly. We did it this way...
NOTE:
Filters / paging which we can influence on the <set> mapping are static filter in the where clause and style of fetching values.
Where clause will always be evaluated when loading ClassBCollection as a property of the ClassA. It could be like where="IsActive=true"
In case that ClassA can have a lot of items in ClassBCollection, we can manage how they will be loaed. Very effective way is attribute batch-size documented here
Can I customize querying of nhibernate set somehow?
I'm not entirely sure what this means. If you meant, can I query and use WHERE clauses over the collection, the answer is yes. Here's how:
[TestFixture]
public class StackOverflowQuestion13496270Tests
{
public ISession session;
[SetUp]
public void SetUp()
{
session = // Get the current NHibernate session
}
[Test]
public void Query_ClassA()
{
var results = session.Query<ClassA>()
.Where( x => x.ClassBCollection.Any( y => y.Name == "Bob" ) )
.Fetch( x => x.ClassBCollection )
.Skip( 0 )
.Take( 50 )
.ToList();
}
[Test]
public void Query_ClassB()
{
var results = session.Query<ClassB>()
.Where( x => x.Name == "Bob" )
.Fetch( x => x.ClassAParent )
.Skip( 0 )
.Take( 50 )
.ToList();
}
public class ClassA
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual IList<ClassB> ClassBCollection { get; set; }
}
public class ClassB
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
// Add this and the appropriate mapping modifications to be able to navigate back to the parent
public virtual ClassA ClassAParent { get; set; }
}
}
I am trying to understand the NHibernate SQL output for this entity:
public class Person
{
public virtual long Id { get; set; }
public virtual long Number { get; set; }
}
When I write:
var maxNumber = s.Query<Person>().Max(p => p.Number);
The generated SQL looks as follows:
select cast(max(person0_.Number) as BIGINT) as col_0_0_ from Person person0_
Why is the cast needed if the Number column is bigint anyway?
I am using NHibernate 3.2, SQL Server 2008 R2, mapping defined as follows:
<class name="NhMappingTest.Person, NhMappingTest">
<id name="Id"><generator class="increment" /></id>
<property name="Number" />
</class>
It's not needed. It's an implementation detail:
protected HqlTreeNode VisitNhMax(NhMaxExpression expression)
{
return _hqlTreeBuilder.Cast(
_hqlTreeBuilder.Max(
VisitExpression(expression.Expression).AsExpression()),
expression.Type);
}
So, the code is blindingly casting the result.
I have an entity where a composite id is used. I changed to code to make use of wrapping the composite id in a seperate key class. I expected that with Linq I could do a comparison on key object and with the Criteria API to use Restrictions.IdEq but both fail. I need to explicitly compare the key values to make it work.
I cannot find any documentation if this should work so for the moment I am stuck with direct comparisons but this means that when I alter the key that I also need to update the query code which is obviously not what I would want.
As a side note, I tried this with NHibernate 3.0.0 Alpha 2 and 3.
Domain
Mapping
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="Cwc.Pulse.Dal"
namespace="Cwc.Pulse.Dal">
<class name="AddonStatus">
<composite-id name="Id">
<key-many-to-one name="Context" column="Context_Id" class="Context" />
<key-property name="AddonType" column="Addon_Id"/>
</composite-id>
<property name="Status" />
</class>
</hibernate-mapping>
Class
public class AddonStatus
{
public virtual string Status { get; set; }
public virtual Key Id { get; protected set; }
public AddonStatus()
{
Id = new Key();
}
public class Key
{
public virtual Context Context { get; set; }
public virtual AddonType AddonType { get; set; }
public override int GetHashCode()
{
return ContextId.GetHashCode() ^ AddonType.GetHashCode();
}
public override bool Equals(object obj)
{
if (this == obj) return true;
var o = obj as Key;
if (null == o) return false;
return Context == o.Context && AddonType == o.AddonType;
}
}
}
Working queries
The queries below work and as you can see I compare the key values explicitly. I do not compare the key object.
Linq
from status
in session.Query<AddonStatus>()
where status.Id.Context == context && status.Id.AddonType == addonType
select status
Criteria API
session.CreateCriteria<AddonStatus>()
.Add(Restrictions.Eq("Id.Context", context))
.Add(Restrictions.Eq("Id.AddonType", addonType))
Expected to work but dont
I expect the following queries to work. Either in efficiently for linq in memory instead of the database but I expect the criteria api to be smart enough to handle such composite id´s in queries.
Both linq and criteria api queries make use of a Key object comparison.
var key = new AddonStatus.Key
{
Context = context,
AddonType = addonType
};
Linq
from status
in session.Query<AddonStatus>()
where status.Id == key
select status
Criteria API
session.CreateCriteria<AddonStatus>()
.Add(Restrictions.IdEq(key))
So if anyone has such a scenario working then what am I doing wrong?
Not directly an answer to your question, but it may be useful to you anyway. You could avoid the (explicit) composite key by mapping the AddonStatus as composite-element on the owner (most probably the Context):
<class name="Context">
<map name="AddonStates" table="AddonStatus">
<key column="Context_Id" /> <!-- Foreign key to the Context -->
<index column="Addon_Id" /> <!-- Dictionary key -->
<composite-element>
<property name="Status" /> <!-- data -->
</composite-element>
</map>
</class>
In the class Context is looks like this:
class Context
{
IDictionary<AddonType, AddonStatus> AddonStates { get; private set; }
}
This results and pretty the same database structure, but it is different to work with. I can't say if this is what you actually want, but it just looks like it.
Interestingly, I'm getting almost the exact opposite of this behavior in 2.1.2.
My mapping (simplified):
<!-- Subscriber class -->
<class name="Subscriber" >
<composite-id name="SubscriberKey" class="SubscriberKey">
<key-property name="Request" column="RequestID" type="int"/>
<key-many-to-one name="User" column="UserID" class="User" not-found="ignore" />
</composite-id>
<!-- User class - note that this goes to a different schema,
and is not mutable. Who knows if that's important... -->
<class name="User" schema="AnotherDb.dbo" mutable="false">
<id name="Id" column="UserID" type="int">
<generator class="native" />
</id>
<property name="FirstName" column="FirstName" type="string" />
<property name="LastName" column="LastName" type="string" />
goes to:
public class User
{
public virtual int? Id {get; protected set;}
public virtual string FirstName { get; protected set; }
public virtual string LastName { get; protected set; }
public User() { }
}
public class Subscriber
{
public virtual SubscriberKey SubscriberKey { get; set; }
public virtual User User { get; set; }
public Subscriber() { }
}
public class SubscriberKey
{
public override bool Equals(object obj)
{
if (obj is SubscriberKey && obj != null)
return ((SubscriberKey)obj).Request == Request
&& ((SubscriberKey)obj).User.Id == User.Id;
return false;
}
public override int GetHashCode()
{
return (Request.ToString() + User.Id.ToString()).GetHashCode();
}
public virtual int Request { get; set; }
public virtual User User { get; set; }
public SubscriberKey() { }
}
Things which work:
CreateCriteria<Subscriber>()
.Add(Restrictions.IdEq(keyInstance))
.UniqueResult<Subscriber>();
CreateCriteria<Subscriber>()
.Add(Restrictions.Eq("SubscriberKey.User.Id", aUserID))
.Add(Restrictions.Eq("SubscriberKey.Request", aRequestID))
.UniqueResult<Subscriber>();
Things which don't work:
Get<Subscriber>(keyInstance);
I'm thinking this is an inconsistency between their various ID-equaling query forms. When I get time, I'll be building a minimal unit test to submit as a bug example. I'd be interested in any / all thoughts anyone might have on this...
edit: Heeey, I figured it out!
Things which do work, now that I've read this
Get<Subscriber>(new SubscriberKey() {
User = Load<User>(aUserID), // the important part!
Request = aRequestID
});
This will create a proxy object for the User key, without hitting the database (unless necessary). If you swap Load<User> for Get<User>, you'll immediately hit the database to populate the object, rather than respecting your lazy-loading properties. Use Load.
And things like this are precisely why people suggest the (type)Repository pattern - I can do this behind the scenes: Get<>(new SK(){User=Load<>(key.User.Id)}, and still Get(key) by a single key, identical to every other object.
I have a class LINE which contains two properties of type POINT.
I would like POINT to be a component property.
If LINE were to contain only 1 POINT, this would be no problem, but since it contains 2 POINTs I would think I needed to distinguish them (so a prefix or suffix can be applied to column names).
I tried using the PropertyName attribute of the ComponentProperty tag, but still only one set of X and Y columns is generated inside my LINE table.
For clarity, my goal is to have a LINE table with a Point1_X, Point1_Y, Point2_X and a Point2_Y column.
I use Nhibernate.Mapping.Attributes, below you can see my mapping
[Class]
public class Line : EntityBase
{
[ComponentProperty(PropertyName = "Point1")]
public UiPoint Point1 { get; set; }
[ComponentProperty(PropertyName = "Point2")]
public UiPoint Point2 { get; set; }
//omitted the constructor
}
[Component]
public class UiPoint
{
[Property]
public double X { get; set; }
[Property]
public double Y { get; set; }
//omitted the constructor
}
In the meanwhile I figured out the following XML mapping wil solve my problem
<class name="Domain.WashProcessLine,Domain">
<id name="Id" />
<component name="Point1">
<property name="X" type="Double" column="Point1_X" />
<property name="Y" type="Double" column="Point1_Y" />
</component>
<component name="Point2">
<property name="X" type="Double" column="Point2_X" />
<property name="Y" type="Double" column="Point2_Y" />
</component>
</class>
found an option on https://www.hibernate.org/hib_docs/nhibernate/html/components.html
the following tagging creates the desired table structure, but does give me a casting exception (from UiPoint to IDictionary) when retreiving the property from the database.
So I'm not entirely there yet :(
[Class]
public class Line : EntityBase
{
[DynamicComponent(1)]
[Property(2, Name = "X", Column = "Point1_X", TypeType = typeof(double))]
[Property(3, Name = "Y", Column = "Point1_Y", TypeType = typeof(double))]
public UiPoint Point1 { get; set; }
[DataMember]
[DynamicComponent(1)]
[Property(2, Name = "X", Column = "Point2_X", TypeType = typeof(double))]
[Property(3, Name = "Y", Column = "Point2_Y",TypeType=typeof(double))]
public UiPoint Point2 { get; set; }
}
After looking at the unit tests for Nhibernate.Mapping.Attributes, and trying a number of different solutions, we found that the cleanest way (unfortunately) to fix the situation provided above, was by injecting some raw xml into our mapping. This means we removed the property attributes in our line class and replaced them with a single entry as shown below
[RawXml(After=typeof(ComponentAttribute), Content = #"<component name=""Point1"">
<property name=""X"" type=""Double"" column=""Point1_X"" />
<property name=""Y"" type=""Double"" column=""Point1_Y"" />
</component>
<component name=""Point2"">
<property name=""X"" type=""Double"" column=""Point2_X"" />
<property name=""Y"" type=""Double"" column=""Point2_Y"" />
</component>")]
This question and answer helped me out while playing around with NHibernatePets Sample
I thought I'd have a go at including the above Line/Point implementation. My code is below if anyone is interested. I found some frustrating quirks with using Attributes too. The first being that if you are using multiple classes with [Id] declarations such as:
[Id(Name = "id")]
[Generator(1, Class = "native")]
then you need to specify the order number (1) for the Generator or else the generated mapping may omit the Generator attribute for one or more of your classes. Apparently this has something to do with the way VS handles things.
Another thing I found when comparing the sample Pet.hbm.xml file with the generated file output using:
//Export to a mapping file when required. Test/Production.
HbmSerializer.Default.Serialize(typeof(Pet).Assembly,"Pets.hbm.xml");
was that the Access="field" property shouldn't be set in the [Id] attribute, even though it was in the sample mapping file.
Line and UiPoint Classes (in NHibernatePets namespace)...
[Class(Lazy = true)]
public class Line
{
[Id(Name = "id")]
[Generator(1, Class = "native")]
#if useAttributes
virtual public int id { get; set; }
#else
private int id;
#endif
const string point1 =
#"<component name= ""Point1"" class= ""NHibernatePets.UiPoint"" >
<property name=""X""
type=""Double""
column=""Point1_X""/>
<property name=""Y""
type=""Double""
column=""Point1_Y""/>
</component>";
const string point2 =
#"<component name=""Point2"" class=""NHibernatePets.UiPoint"" >
<property name=""X""
type=""Double""
column=""Point2_X""/>
<property name=""Y""
type=""Double""
column=""Point2_Y""/>
</component>";
[RawXml(After = typeof(ComponentAttribute), Content = point1)]
virtual public UiPoint Point1 { get; set; }
[RawXml(After = typeof(ComponentAttribute), Content = point2)]
virtual public UiPoint Point2 { get; set; }
}
//Don't need any Attributes set on this class as it's defined in the RawXml.
public class UiPoint
{
public double X { get; set; }
public double Y { get; set; }
}
In Main()...
//Create the Line record
Line newLine = new Line
{
Point1 = new UiPoint { X = 100.1, Y = 100.2 },
Point2 = new UiPoint { X = 200.1, Y = 200.2 }
};
try
{
using (ISession session = OpenSession())
{
using (ITransaction transaction = session.BeginTransaction())
{
session.Save(newLine);
transaction.Commit();
}
Console.WriteLine("Saved NewLine to the database");
}
}
catch (Exception e)
{ Console.WriteLine(e); }
In Public Class Program...
static ISessionFactory SessionFactory;
static ISession OpenSession()
{
if (SessionFactory == null) //not threadsafe
{ //SessionFactories are expensive, create only once
Configuration configuration = new Configuration();
#if useAttributes
{
configuration.SetDefaultAssembly("NHibernatePets");
//configuration.SetDefaultAssembly(System.Reflection.Assembly.GetExecutingAssembly().ToString());
//To use Components and other structures, AssemblyName must be set.
//configuration.SetDefaultAssembly(typeof(Pet).Assembly.ToString());
configuration.AddInputStream(NHibernate.Mapping.Attributes.HbmSerializer.Default.Serialize(typeof(Pet).Assembly));
}
#else
configuration.AddAssembly(Assembly.GetCallingAssembly());
#endif
//Export to a mapping file when required. Test/Production.
HbmSerializer.Default.Serialize(typeof(Pet).Assembly,"Pets.hbm.xml");
SessionFactory = configuration.BuildSessionFactory();
}
return SessionFactory.OpenSession();
}
Assume the following entity classes:
public class Player
{
public virtual int ID { get; set; }
public virtual string Name { get; set; }
public virtual Team Team { get; set; }
}
public class Team
{
public virtual int ID { get; set; }
public virtual string City { get; set; }
public virtual string Nickname { get; set; }
}
Assume the following mapping class for Player:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" default-lazy="false">
<class name="Player">
<id name="ID" column="ID" type="System.Int32" unsaved-value="null">
<generator class="native"/>
</id>
<property name="Name" column="Name" not-null="true" type="System.String" length="50" insert="true" update="true"/>
<many-to-one name="Team" not-null="true" outer-join="auto" insert="true" update="true">
<column name="TeamID"/>
</many-to-one>
</class>
</hibernate-mapping>
And assume the following Player repository method:
public void Add(Player player)
{
using (ISession session = NHibernateHelper.OpenSession())
{
using (ITransaction transaction = session.BeginTransaction())
{
session.Save(player);
transaction.Commit();
}
}
}
My question:
Must I load a full-fledged Team (parent object) when I want to create a new Player?
Or can I specify a "mock" object, and only specify the foreign key?
Player player = new Player
{
Name = "Tom Brady",
Team = new TeamRepository().GetTeamByCityAndNickname("New England", "Patriots") // Is this the only way?
// or can I do this?
// Team = new Team { ID = 22 }
};
new PlayerRepository().Add(player);
And if I can't specify a "mock"
object (specifying only the
foreign key), can you please explain
why I can't?
That is, can you please give me an idea about what's going on under the hood?
Heads-up:
A fellow had nearly the same question.
Here's the answer that made the most sense.
Interestingly, when speaking about EF
4.0 during a DotNetRocks episode, Julia Lerman
acknowledged that many people want to
use the foreign key in these types of
situations.
EDIT: This answer points to the essence of my question.
Think of it like having an object that
only keeps the Id and that will load
the rest if you ever need it. If
you're just passing it arround to
create relationships (like FKs), the
id is all you'll ever need.
Well if that's the case, then why do I need to be worried about proxy objects and such? Why can't I just create a "dummy" object and specify the foreign key value if that's all that really matters?
you use the foreign key like so...
Team = session.Load<Team>(id);
know the difference between load and get
If you have access to Session at this point you can call
Team = Session.Load<Team>(id);
The premise of Load is that it will create an NHibernate proxy that can resolve itself if needed. Of course you have to be sure the id exists or you will get an EntityNotFound error if it ever tries to resolve itself.