views:

330

answers:

3

Hi,

My object looks something like this:

class
{
    int a;
    object b;
    IList<string> c;
}

All the fields are getting populated from the database and the collection is getting lazy initialization which is desirable. Now, my problem is that I want to send this object to the web service. But since the collection is lazily loaded, am not able to do it. Can somebody please give me an idea or a direction or some example code which I can look into for my problem.

I want a generic way to force initialization for this list before I send it over the wire. Also, I have multiple objects like this so a generic way to do it would be great. Right now, to do this I am using this method:

public static T Unlazy<T>(this T persistentCollection)
{

    if (persistentCollection is IPersistentCollection) 
    { 
        IPersistentCollection collection = (IPersistentCollection)persistentCollection;             
        collection.SetCurrentSession(session.GetSessionImplementation()); 
        collection.ForceInitialization(); 
    } 
}  

But this throws an object reference set to null exception for some reason

Thanks.

A: 

I'm not sure if I understood the question correctly. You have a database on the server side - which you use NHibernate with - using lazy loading. The you get problems with your objects when transdered to the client side?

If this is the case what you need to do is stop lazy loading. As the client doesn't have access to the Database you won't be able to load the lazy loaded data from the client side. You need to make sure the data is loaded on the server side before transfering it to the client. The simplest way of solving this is to stop lazy loading. If not you need to ensure the lazy data is loaded before the holding object is returned to the client. If you have a service that fetches data from the database, does something, and returns this data you might just as well load your data from the database right away.

Hope this helps.

stiank81
Well .. I have to pass the data from the client side to the server. And this object that I talked about above is the one I need to send. I do not want to stop lazy loading during the mapping part because that would just result in too many queries and is undesirable. I want a generic way to force initialization for this list before I send it over the wire. Also, I have multiple objects like this so a generic way to do it would be great. Right now, to do this I am using this method:
Saurabh Lalwani
public static T Unlazy<T>(this T persistentCollection) { { if (persistentCollection is IPersistentCollection) { IPersistentCollection collection = (IPersistentCollection)persistentCollection; collection.SetCurrentSession(session.GetSessionImplementation()); collection.ForceInitialization(); } }}But this throws an object reference set to null exception for some reason.
Saurabh Lalwani
Ok. I don't know any better solution than James on how to unlazy this if you want to keep lazy loading in your mappings.
stiank81
+2  A: 

There is no magic bullet to 'unlazy' a collection. You would most likely trigger the SELECT+1 problem. You really need to perform an eager load on object graph.

You can do this in your mappings, but I recommend that you leave everything in your mappings as lazy. The best policy is to override this behavior when you fetch from the database. If using HQL, the query is something like

"from class 
left join **fetch** class.b b
left join **fetch** class.c c"

If you're using ICriteria, use SetFetchMode().

You will notice duplicate root entity objects when you perform List(). This is expected behavior. If this is not what you want, the easiest solution is to load everything into a HashedSet and enumerate the results.

N.B. You can only eagerly load one collection per entity. This is a limitation of SQL.

James L
Well.. actually I do not want to eagerly fetch those collections until I send them. And since I have multiple objects to send which have lazily loaded collections so I am looking for a generic way to do it rather than having to do it for each object separately. The ForceInitialization() method for the persistentCollections seemed to be the best way to me but it's not working for some reason.
Saurabh Lalwani
That solution would cause the N+1 problem. I still think you're best of using eager fetching, even if you have to perform the query a second time.
James L
A: 

Short answer: though it may pain you (as it did me) to have to hold NHibernate's hand more than you wanted to, I concur with James L that eager fetching seems to be the best way to do it.

As per Mauricio's links, we've found that passing NHibernate entities across WCF directly doesn't work too well anyway, since you're not really passing objects the POCOs that WCF loves, but proxied objects with NHibernate-specific collections.

For that reason we've had success with using Automapper to convert an entity to a corresponding DTO for passing across the wire with WCF. A side-effect of this is that it basically un-lazies everything simply because it's going to traverse the object graph in order to put it in another object.

Though that works, I have to strongly agree with James' warning regarding the select N+1 problem; we've found in our web app that a single page load can result in hundreds of individual database calls. You can really cut a huge number out by using eager fetching with HQL or Criteria first.

You can only eagerly load one collection per entity. This is a limitation of SQL.

I'm not sure this is true, but I would certainly recommend that you load the collections in separate queries; I think you can load them all in one query, but in the underlying SQL they're going to be left outer joins which will result in a Cartesian product that can very easily get out of control.

As usual, Ayende Rahien has described all of this very well already, as well as description of how to use Futures to at least do all of those separate collection queries in one round-trip with relative ease.

Gavin Schultz-Ohkubo