Customization of nhibernate <set> querying - nhibernate

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

Related

How best to inject value translation into an NHibernate property?

I have a model like this:
public class Order
{
public virtual int OrderType { get; set; }
}
(lots of other properties omitted of course) which maps directly to an int type in the DB.
The thing is, the numeric order type is meaningless to my application. There are single-letter codes that the user sees which denote the order type. So, I could do something like this:
public class Order
{
public virtual int OrderTypeIgnored { get; set; }
public virtual char OrderType
{
get
{
return translateForward(OrderTypeIgnored);
}
set(char val)
{
OrderTypeIgnored = translateBackward(val);
}
}
}
(lots of air code/pseudocode there, I'm relatively new to C#) and just map the OrderTypeIgnored property. But is there a cleaner way to do this? Perhaps somehow overriding the getter and setter on the mapped property itself?
A few notes: The values are static enough that embedding the translation in the code is not a problem. No, there's no LOV table, and no, I don't have control over the database structure.
Sorry if there are answers for this, but searching for things like "mapping" and "translation" don't really get me the results I'm looking for, obviously.
You could create a public char property that uses a private int field and only map the field.
Model:
public class Order
{
private int _orderType;
public virtual char OrderType
{
get
{
return TranslateForward(_orderType);
}
set
{
_orderType = TranslateBackward(value);
}
}
}
Mapping:
<property name="_orderType" access="field" />
If you don't want to map the field directly (because you use a compile-safe mapping) you can map the public property using the access strategy "field", a naming strategy like "camelcase-underscore" and explicitly specify the "Int32" type.
you can always use enums for this kind of situation.
You can define it like this:
namespace MyApp.Domain
{
using System.ComponentModel;
public enum OrderType : short
{
[Description("Order Suspended")]
Suspended = 1,
[Description("Order Delivered")]
Delivered = 2,
[Description("Order New")]
Inserted = 3
}
}
and map it this way:
<property name="Type" type="MyApp.Domain.OrderType, MyApp.Domain" >
<column name="Type" not-null="true"/>
</property>
so you can write your QueryOver in a simple way like this:
var orders = this.Session.QueryOver<MyApp.Domain.Orders>()
.Where(x => x.Type == MyApp.Domain.OrderType.Inserted)
.List();

Composite Id's and Restrictions.IdEq or with comparison in Linq not working as expected

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.

Can I have a collection of IUserType instances?

Is it possible to have a collection of entities that are mapped via an IUserType? For example:
class Foo
{
public int Id { get; set; }
public ISet<Special> Items { get; set; }
}
class Special
{
public Special(string columnValue)
{
_val = columnValue;
}
public override string ToString()
{
return _val.TranformInInterestingWays();
}
private string _val;
}
class SpecialUserTypeForMapping : IUserType
{
// assume that all the right stuff is here
// for using the non-default constructor on the way in
// and calling ToString() on the way out
}
If I'm reading the documentation correctly, neither the <set>, <one-to-many>, <many-to-many>, nor <class> element supports the "type" attribute as is used to apply IUserType mapping to <property>s. So how would I go about mapping this?
The most expedient solution seems to be to use the <element> element, like so:
<class name="Foo" table="foos">
<id name="Id" />
<set name="Items" table="foo_special">
<key column="fooId" />
<element column="special_value" type="SpecialUserTypeForMapping" />
</set>
</class>
Retrieving different Foo instances from the DB is no problem, but it's not clear whether it's possible to write queries against the special_value column, which is a requirement in my scenario. This question seems to indicate that it's not.

How to save a child with assigned id in nhibernate

I have two classes:
public class Parent
{
public virtual long? ID { get; set; } // native
public virtual IList<Child> Children { get; set; }
public virtual string Name { get; set; }
}
public class Child
{
public virtual long ID { get; set; } // assigned
public virtual string Name { get; set; }
}
Instantiating and saving parent and child:
child = new Child() { ID = 1, Name = "SomeName" };
parent = new Parent() { Children = new List() { child } };
session.Save(parent);
Which gives me:
NHibernate.StaleStateException: Unexpected row count: 0; expected: 1.
I think the problem is with the assigned id on the child. Since it has an id, NHibernate thinks it has previously saved before which is not the case.
The generated (trimmed & renamed) SQL is:
NHibernate: select child0_.ID as child1_1_, child0_.NAME as NAME1_, child0_.PARENT_ID as COMMAND7_1_, from CHILD child0_
NHibernate: select parent0_.PARENT_ID as parent1_10_
NHibernate: select parent0_.PARENT_ID as parent1_10_, parent0_.NAME as parent2_10_ from PARENT parent0_
NHibernate: UPDATE CHILD SET PARENT_ID = #p0 WHERE CHILD_ID = #p1;#p0 = 2, #p1 = 1
Mapping files:
<class name="MyNamespace.Child" table="CHILD">
<id name="ID" column="CHILD_ID" type="System.Int64">
<generator class="assigned"></generator>
</id>
<property name="Name" column="NAME"></property>
</class>
<class name="MyNamespace.Parent" table="PARENT">
<id name="ID" column="PARENT_ID" type="System.Int64">
<generator class="native"></generator>
</id>
<property name="Name" column="NAME"></property>
<bag name="Children">
<key column="PARENT_ID"></key>
<one-to-many class="MyNamespace.Child"></one-to-many>
</bag>
</class>
While searching google, I found about version tag which may be a solution but I do not have a persistent field to use as version. In this case, how can I save (insert) a child with assigned id and its parent?
When cascading from a parent to a child, NHibernate uses the SaveOrUpdate method. You are correct that NHibernate need some way to determine whether it should perform an insert or an update. It will look at three different fields for an unsaved value to determine if the entity is new.
Id
Version
Timestamp
With an assigned Id, you will need either a Version or Timestamp field in order to indicate that the entity is new.
An alternative would be to call Save() on the children explicitly.
I'm not 100% sure if this is the same problem you are having, but my database is 100% assigned id (ugh) and I had to create an Interceptor that kept track of whether a child is or isn't persisted for cascades to work.
The code is cut/paste (which is why it has dumb names... I didn't understand 100% at first!), and I initially got 90% of it from online documentation (which I can't find via google right now ... sorry):
Base class you put on object that has an assigned ID you want to cascade:
public class Persistent
{
private bool _saved = false;
public virtual void OnSave()
{
_saved = true;
}
public virtual void OnLoad()
{
_saved = true;
}
public virtual bool IsSaved
{
get { return _saved; }
}
}
The interceptor you add to session:
public class TrackingNumberInterceptor : EmptyInterceptor
{
public override bool? IsTransient(object entity)
{
if (entity is Persistent)
{
return !((Persistent)entity).IsSaved;
}
else
{
return null;
}
}
public override bool OnLoad(object entity, object id, object[] state, string[] propertyNames, IType[] types)
{
if (entity is Persistent) ((Persistent)entity).OnLoad();
return false;
}
public override bool OnSave(object entity, object id, object[] state, string[] propertyNames, IType[] types)
{
if (entity is Persistent) ((Persistent)entity).OnSave();
return false;
}
}
Basically the idea is that since NHibernate doesn't know if an assigned id entity is persisted or not, you keep track for it.
By default the object starts with persisted (_saved) at false. When the entity is either loaded or saved by NHibernate, the trigger sets the objects persisted (_saved) flag to true.
So for a fresh item that isn't persisted, it starts at false and stays false because NHibernate has never saved or loaded it. When NHibernate checks whether the child is transient, the trigger responds that it is transient, and a save happens which marks the child as persisted. Also now any future use will require a load which again marks it as persisted.
Calling session.SaveOrUpdate( childObject ) should solve the problem.

NHibernate: how to enable lazy loading on one-to-one mapping

One-to-one relations within nhibernate can be lazyloaded either "false" or "proxy". I was wondering if anyone knows a way to do a lazy one-to-one mapping.
I worked out a hack to achieve the same result by using a lazy set mapped to a private field, and having the public property return the first result of that set. It works, but isn't the cleanest code...
Thanks in advance!
Lazy loading of one-to-one isn't supported unless the association is mandatory. See here for the reasoning.
It boils down to the fact that in order to decide if the other side of the relationship exists (N)Hibernate has to go to the database. Since you've already taken the database hit, you might as well load the full object.
While there are cases where hitting the DB just to see if the related object exists without actually loading the object makes sense (if the related object is very "heavy"), it isn't currently supported in NHibernate.
As far as I know, there isn't a non-hacky way to lazy load a one-to-one. I hope I'm wrong, but last time I checked it was the case.
There is way thought. It's described here in details :
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" assembly="NHibernateTest" namespace="NHibernateTest">
<class name="Person" >
<id name="PersonID" type="Int32">
<generator class="identity" />
</id>
<property name="LastName" type="String" length="50" />
<property name="FirstName" type="String" length="50" />
<many-to-one name="Photo" class="PersonPhoto" />
</class>
<class name="PersonPhoto">
<id name="PersonID" type="Int32">
<generator class="foreign">
<param name="property">Owner</param>
</generator>
</id>
<property name="Photo" type="BinaryBlob" />
<one-to-one name="Owner" class="Person" constrained="true" />
</class>
</hibernate-mapping>
I tried the example used by Artem Tikhomirov above. I kept getting an error that the Photo column does not exist. After looking at this, I figured out that the mapping was off a little. When I changed the many-to-one mapping to specify the column name like this:
many-to-one name="Photo" column="PersonID" class="PersonPhoto" unique="true"
I got it to work. I hope this helps someone :o)
After reading the answers here, I´ve manage to get it to work.
I´m just going to add this example because I´m using a One to One relation with Constrained= False and because it´s a Mapping by Code Example
Two Classes:
public class Pedido:BaseModel
{
public virtual BuscaBase Busca { get; set; }
}
public class BuscaBase : BaseModel
{
public virtual Pedido Pedido { get; set; }
}
Mappings:
public class PedidoMap : ClassMapping<Pedido>
{
public PedidoMap()
{
Id(x => x.Id, x => x.Generator(Generators.Identity));
ManyToOne(x => x.Busca, m =>
{
m.Cascade(Cascade.DeleteOrphans);
m.NotNullable(true); m.Unique(true);
m.Class(typeof(BuscaBase));
});
}
}
public class BuscaBaseMap : ClassMapping<BuscaBase>
{
public BuscaBaseMap()
{
Id(x => x.Id, x => x.Generator(Generators.Sequence, g => g.Params(new { sequence = "buscaefetuada_id_seq" })));
OneToOne(x => x.Pedido, m =>
{
m.Lazy(LazyRelation.NoProxy);
m.Constrained(false);
m.Cascade(Cascade.None);
m.Class(typeof(Pedido));
});
}
}
Note: I was Using for the one-to-one mapping m.PropertyReference(typeof(Pedido).GetProperty("Busca")); but this does't work for the lazy loading. You have to specify the relation using the Class
A quick brief about the Constrained = False used in here, the "Pedido" object might not exist in "BuscaBase" object.
What worked for me is the following (very similar to #Daniel) but I found that it was necessary to specify the LazyRelation.NoProxy on both ends of the mapping.
public class Person
{
public virtual int Id { get; set; }
public virtual string Name { get; set; }
public virtual PersonDetails Details { get; set; }
public class PersonMap : ClassMapping<Person>
{
public PersonMap()
{
Id(x => x.Id, m =>
{
m.Generator(Generators.Native);
});
Property(x => x.Name);
OneToOne(x => x.Details, m =>
{
m.Lazy(LazyRelation.NoProxy);
m.Cascade(Cascade.Persist);
});
}
}
}
public class PersonDetails
{
public virtual int Id { get; set; }
public virtual string ExtraDetails { get; set; }
public virtual Person Person { get; set; }
public class PersonDetailsMap : ClassMapping<PersonDetails>
{
public PersonDetailsMap()
{
Id(x => x.Id, m =>
{
m.Generator(Generators.Native);
});
Property(x => x.ExtraDetails);
ManyToOne(x => x.Person, m =>
{
m.Lazy(LazyRelation.NoProxy);
m.Unique(true);
m.NotNullable(true);
});
}
}
}
using var session = NhHelper.OpenSession();
var person1 = new Person();
person1.Name = "A";
var person1Details = new PersonDetails();
person1Details.ExtraDetails = "A details";
person1.Details = person1Details;
person1Details.Person = person1;
session.Save(person1);
//because of PersonMapping's Cascade.Persist it is not necessary to manually save person1Details object.
using var session = NhHelper.OpenSession();
foreach(var person in session.Query<Person>()) {
Console.WriteLine(person.Name); //<-- does not load PersonDetails unless it's property is accessed
}
NHibernate 5.3.5
Npgsql 5.0.3 (Postgresql Db).