Parallel.Forech using NHibernate

1,412 views
Skip to first unread message

Calin

unread,
Sep 7, 2010, 4:53:22 AM9/7/10
to nhusers
Hi,

I am basically trying to take a long reading operation and make good
use of the servers multi core processor. The basic serial operation
looks like this:


foreach (TEntity entity in entities)
{
TBos bos = factory.Create(entity);
list.Add(bos);
}

factory.Create will take the entity and create a bussiness object out
of it, in the process it might call thinks like entity.Categories or
entity.Products witch are lazy loaded collections.

Now if I take all this and wrap it in a Parallel.Foreach(...) it will
randomly crush when accessing the lazy loaded collections with "null
object references". I am pretty sure is related to session management
but I can seem to find a example of how to manage the session in multi
threat environment.

So the question is, can anyone point me to a reference implementation
that uses Nhibernate and multi-threading to read data ?

Thank you very much,

Jason Dentler

unread,
Sep 7, 2010, 8:03:40 AM9/7/10
to nhu...@googlegroups.com
Don't do this. Sessions are not thread safe. 


--
You received this message because you are subscribed to the Google Groups "nhusers" group.
To post to this group, send email to nhu...@googlegroups.com.
To unsubscribe from this group, send email to nhusers+u...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/nhusers?hl=en.


Graham Bunce

unread,
Sep 7, 2010, 9:28:00 AM9/7/10
to nhusers
... however if you did want to do this then you'd need some framework
to manage the sessions for you.

the best way may be to wrap each Create within a method that manages
the session for you e.g. (semi pseudo-code)

Parallel.ForEach(() => { ServiceLayer.Create(entity); };

ServiceLayer.Create<T>(T entity)
{
try
{
NH.Session.Open();
//Do Stuff
}
finally
{
NH.Session.Close();
}
}

Or something along these lines. Make sure that the entity you pass is
either a DTO (safer IMO) or you attach it to the new session within
the Create method. There may be some quirks with the latter though.
I've done some multi-threaded stuff with NH but not a huge amount so
there might be a problem with the above... but I've done something
similar myself and, so far, no issues I've come across.

nadav s

unread,
Sep 7, 2010, 10:22:07 AM9/7/10
to nhu...@googlegroups.com
hi Calin. The main performance issue with your code isn't lake of use of your multi core proccessor, but a classical N+1 problem.

you're enumerating an enumeration of entities, lazy loading their collection, now unless you have set a big batch size, if you have 100 entities, and you are have one one-to-many relationship, you're gonna have here 101 queries (thus N+1) - one for the entities (the previous query) and 1 query per a returned entity.

you can actually just query for the products of all you're entities, and then you'll have only 2 queries, one fetching the entities, and one fetching the products.

by the way, why don't you put your domain business logic inside your nhibernate mapped entities?


--

Calin

unread,
Sep 7, 2010, 1:07:36 PM9/7/10
to nhusers
Hi Nadav,

"you can actually just query for the products of all you're
entities," This looks the most appealing, how do I actually do this ?
You mean I shoud call a FetchAll for entities and FetchAll for
products and then the lazy loader for entities will now how to load
entity.Products based on the collection that I fetched previously ?

"by the way, why don't you put your domain business logic inside your
nhibernate mapped entities? "
Yes, why don't I ? It has something to do with my business layer, I
find it very hard to map collections into my business object, I use
CSLA, and there are some issues here specific to my application.

On Sep 7, 5:22 pm, nadav s <nadav...@gmail.com> wrote:
> hi Calin. The main performance issue with your code isn't lake of use of
> your multi core proccessor, but a classical N+1 problem.
>
> you're enumerating an enumeration of entities, lazy loading their
> collection, now unless you have set a big batch size, if you have 100
> entities, and you are have one one-to-many relationship, you're gonna have
> here 101 queries (thus N+1) - one for the entities (the previous query) and
> 1 query per a returned entity.
>
> you can actually just query for the products of all you're entities, and
> then you'll have only 2 queries, one fetching the entities, and one fetching
> the products.
>
> by the way, why don't you put your domain business logic inside your
> nhibernate mapped entities?
>
> On Tue, Sep 7, 2010 at 4:28 PM, Graham Bunce <grahambu...@hotmail.com>wrote:
>
>
>
>
>
>
>
> > ... however if you did want to do this then you'd need some framework
> > to manage the sessions for you.
>
> > the best way may be to wrap each Create within a method that manages
> > the session for you e.g. (semi pseudo-code)
>
> > Parallel.ForEach(() => { ServiceLayer.Create(entity); };
>
> > ServiceLayer.Create<T>(T entity)
> > {
> >  try
> >  {
> >     NH.Session.Open();
> >     //Do Stuff
> >  }
> >  finally
> >  {
> >     NH.Session.Close();
> >  }
> > }
>
> > Or something along these lines. Make sure that the entity you pass is
> > either a DTO (safer IMO) or you attach it to the new session within
> > the Create method. There may be some quirks with the latter though.
> > I've done some multi-threaded stuff with NH but not a huge amount so
> > there might be a problem with the above... but I've done something
> > similar myself and, so far, no issues I've come across.
>
> > --
> > You received this message because you are subscribed to the Google Groups
> > "nhusers" group.
> > To post to this group, send email to nhu...@googlegroups.com.
> > To unsubscribe from this group, send email to
> > nhusers+u...@googlegroups.com<nhusers%2Bunsu...@googlegroups.com >
> > .

Jason Dentler

unread,
Sep 7, 2010, 3:24:27 PM9/7/10
to nhu...@googlegroups.com

nadav s

unread,
Sep 7, 2010, 3:25:50 PM9/7/10
to nhu...@googlegroups.com
about the 2 seperate queries, you can map your collection with a batch-size="X" , if you have a pretty good idea on how many root entities your gonna fetch in the first query, and nhibernate will automatically, when lazy loading one association, will also load the associations of X other root entities, so it will result in one query per X entities, instead of a query per entity. as of V3, hopefully, you'll be able to set the batch size per session (and not only in the mappings).

if you don't think that batch size will work for you (as it will batch X entities, and not all of them), you can read this blog post:
http://ayende.com/Blog/archive/2010/01/16/eagerly-loading-entity-associations-efficiently-with-nhibernate.aspx

or set a very big batch size (which might not be ok in mappings, but if and when you could do it per session, i think it will be a good practice).




To unsubscribe from this group, send email to nhusers+u...@googlegroups.com.

nadav s

unread,
Sep 7, 2010, 3:34:14 PM9/7/10
to nhu...@googlegroups.com
about CSLA - i've googled it and read a little bit, as i see it you have an Accounts object, that is the collection of accounts, and you're probably having trouble mapping a class like User that has a property of type Accounts, instead of just IEnumerable<Account>?

if i get it correctly, i guess i can understand the problem, but i do advice you to re-concider, because if i understand what you're doing, you'll actually have 2 classes per entity, one that is a no brainer class with just properties mapped to nhibernate and the other is the class with the smart collections and stuff. aren't you doubling you're work? whats for sure is you're not using any of lazy loading features of nhibernate, so you lose some of its power. you should also realize that you have to set the fetch loading of you're many to one mappings to fetch="join" or else you'll always have seperate queries for them, and you're not lazy loading them anyhow (you're factory will always cause the proxy to initiate won't it?)

Calin

unread,
Sep 8, 2010, 1:22:04 AM9/8/10
to nhusers
Hi Nadav,

Thank you for your answer. Indeed CSLA has the problem with Accounts
and IEnumerable<>, and there are cases in witch the work is double, is
a common problem with the CSLA and nhibernate or any other ORM.

But I do use the lazy loading feature in nhibernate, I basically have
2 methods to populate the business objects CreateLigth and Create, one
populates the list with some of the data, the last loads the object
with all the data including all collections.

On Sep 7, 10:34 pm, nadav s <nadav...@gmail.com> wrote:
> about CSLA - i've googled it and read a little bit, as i see it you have an
> Accounts object, that is the collection of accounts, and you're probably
> having trouble mapping a class like User that has a property of type
> Accounts, instead of just IEnumerable<Account>?
>
> if i get it correctly, i guess i can understand the problem, but i do advice
> you to re-concider, because if i understand what you're doing, you'll
> actually have 2 classes per entity, one that is a no brainer class with just
> properties mapped to nhibernate and the other is the class with the smart
> collections and stuff. aren't you doubling you're work? whats for sure is
> you're not using any of lazy loading features of nhibernate, so you lose
> some of its power. you should also realize that you have to set the fetch
> loading of you're many to one mappings to fetch="join" or else you'll always
> have seperate queries for them, and you're not lazy loading them anyhow
> (you're factory will always cause the proxy to initiate won't it?)
>
> On Tue, Sep 7, 2010 at 10:24 PM, Jason Dentler <jasondent...@gmail.com>wrote:
>
>
>
>
>
>
>
>
>
> >http://ayende.com/Blog/archive/2010/01/16/eagerly-loading-entity-asso...
>
> > <http://ayende.com/Blog/archive/2010/01/16/eagerly-loading-entity-asso...>
> >> <nhusers%2Bunsu...@googlegroups.com<nhusers%252Bunsubscribe@googlegroup s.com>>
Reply all
Reply to author
Forward
0 new messages