Composite Id and Restrictions.IdEq or Comparison in Linq not working properly

I have an object that uses a composite identifier. I changed the code to use a composite id wrapper in a separate key class. I expected that with Linq, I could do a comparison on a key object, and using the criteria API use Restrictions.IdEq, but both do not work. I need to explicitly compare key values ​​to make it work.

I can’t find any documentation if it works like this at the moment that I follow direct comparisons, but that means that when I change the key, I also need to update the request code, which is clearly not the one I would like .

As a side note, I tried this with NHibernate 3.0.0 Alpha 2 and 3.

Domain

Display

<?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; } } } 

Work requests

The following are the queries, and as you can see, I am explicitly comparing the key values. 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)) 

It is expected that it will work, but not

I expect the following queries to work. It is either efficient for linq in memory instead of a database, but I expect the api criteria to be smart enough to handle such complex identifiers in queries.

Both linq queries and api criteria use Key object comparisons.

 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 someone has such a scenario, then what am I doing wrong?

+4
source share
2 answers

Interestingly, I am almost completely opposite to 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 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 that 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 that don't work:

 Get<Subscriber>(keyInstance); 

I think this is inconsistency between different forms of ID-peer requests. When I get the time, I will create a minimal unit test to send as an example of an error. I would be interested in any thoughts that everyone may have on this ...


edit: Hey, I get it!

Things that work, now that I 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 getting into the database (if necessary). If you change Load<User> to Get<User> , you will immediately go to the database to fill the object, and not respect your lazy loading properties. Use Load.

And things like this, why people offer a template of a type (type) of a repository - I can do this behind the scenes: Get<>(new SK(){User=Load<>(key.User.Id)} and Get(key) one key identical to any other object.

0
source

The answer directly to your question, but it may be useful to you in any case. You can avoid the (explicit) composite key by matching AddonStatus as a composite element in the owner (most likely 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 Context class, it looks like this:

 class Context { IDictionary<AddonType, AddonStatus> AddonStates { get; private set; } } 

This is a result and a pretty similar database structure, but it is different from work. I can’t say if this is really what you really want, but it just looks.

0
source

All Articles