NHibernate iStatelessSession returns duplicate parent instances eagerly

I am trying to get root entity and eagerly get child objects. But since I am using IStatelessSession

NHibernate for, it returns duplicate root entity for each child. Using ISession it will be solved with

.TransformUsing(new DistinctRootEntityResultTransformer()) 

      

But IStatelessSession

this is not the case.

It is mainly about the code below where there is only one parent instance containing 3 children.

var result = session.QueryOver<Parent>()
.Fetch(i => i.Childs).Eager();

      

This will return 3 duplicate instances of the parent instead of one. Does anyone have a solution for this?

+3


source to share


1 answer


I would say: Do not use StatelessSession. It is not suitable for this use case.

13.2. StatelessSession interface

Alternatively, NHibernate provides a command API that can be used to stream data to and from the database as separate objects. IStatelessSession does not have a persistence context associated with it and does not provide much higher level lifecycle semantics. In particular, a stateless session does not implement a first level cache and does not interact with a second level or request frame. It does not implement transactional write or automatic dirty checking. Operations performed using a stateless session are never cascaded for linked instances. Collections are ignored by the session without persisting. Operations performed by NHibernate Session and Interceptors ...

I just tried to explain that here: NHibernate: choose one for many Left Join - Take X from parent , The problem here is your JOIN as a result of this SQL result, which is not swap-friendly (which you will need sooner or later)

PARENT1 CHILD1
PARENT1 CHILD2
PARENT1 CHILD3
PARENT2 CHILD4
PARENT2 CHILD5 // if we would take 5 records, the parent2 won't get child6
PARENT2 CHILD6

      

Therefore, this result is not appropriate. I highly recommend: use

  • standard session, unused (to kill it immediately)
  • load the list of root object (parent) and
  • let NHibernate load its children lazily - in a separate SQL query.

The request may / should look like this:

ISessionFactory factory = ...;
using (var session = factory.OpenSession())
{
    var list = session.QueryOver<Parent>()
    .Skip(100)
    .Take(25)
    .List<Parent>();

    list.Last() // this will load all occupations at once
        .Childs // if batch-size is higher than page size
        .Any(); // otherwise touch more items

} // session is closed and disposed

      



As you can see from the above code snippet, to avoid the 1 + N problem, we should use one of the smart match functions:

19.1.5. Using batch fetch

NHibernate can efficiently use batch fetching, that is, NHibernate can load multiple uninitialized proxies if one proxy is accessed (or collections). Batch fetch is an optimization of a lazy fetch strategy. There are two ways to customize batch fetch: at the class and collection level.

Batch sampling for classes / entities is easier to understand. Imagine at runtime you have the following situation: you have 25 Cat instances loaded into the ISession, each Cat has a reference to its owner, a human. The Person class maps to the proxy, lazy = "true". If you now iterate over all cats and call cat.Owner on each, NHibernate will execute 25 SELECT statements by default to get proxied owners ...

And the parent mapping should look like this:

HasMany(x => x.Childs)
    ...
    .BatchSize(100) // should be at least 25

      

Please check also:

NOTE. Some people may suggest that you use Result Transforemer as you tried. This solution might work, but is done in C # in memory, so all data is loaded (multiple lines) and then narrowed down. I would never use this. Check out: Criteria.DISTINCT_ROOT_ENTITY vs Projections.distinct

+2


source







All Articles