views:

51

answers:

2

I have Fluent mappings that maps a fictional class Customer to a list of Orders. Now I want to fetch all Customers from the database without loading Orders. Can this be specified somehow in the query/criterion/etc, or is LazyLoading the only solution?

Fictional classes:

public class Customer
{
    public virtual int Id { get; set; }
    public virtual string Name { get; set; }
    public virtual IList<Order> Orders { get; set; }
}

public class Order
{
    public virtual int Id { get; set; }
    // ++ 
}
+2  A: 

Lazy loading does this automatically anyway, are you trying to avoid Lazy loading? Why?

For instance:

IList<Customer> customers = _session.CreateCriteria<Customer>().List<Customer>();

Will give you all customers only. Orders will only be fetched from the database when you call the Order collection on a Customer entity eg:

foreach(Customer customer in customers)
{
    IList<Order> orders = customer.Orders; // Will hit the database
}

So if you have 10 Customers, this code will hit the database 11 times (1 to get the Customer, 10 to retrieve Orders for each Customer). This gives you a SELECT N+1 problem, though if you're not interested in the Orders for each customer, then simply don't call Orders and they won't be fetched. I don't mean to be rude, but this seems fairly obvious, am I misunderstanding your question?


Update: In response to the comments. You should consider using a Report Query if sending your POCO's over a web service, because your POCO's would loose their attachment to your NHibernate Session object, and thus lazy loading won't work (Orders collection will just return NULL). In other words the 'thing' calling your web service properly isn't going to know anything about NHibernate, or your domain model, so:

public IList<CustomerView> GetAllCustomers()
{
    return (from c in _session.Query<Customer>()
            select new CustomerView()
            {
                Id = c.Id,
                Name = c.Name
            }).ToList();
}

public CustomerDetail GetCustomerFromId(int id)
{
    return (from c in _session.Query<Customer>()
            where c.Id == id
            select new CustomerDetail()
            {
                Id = c.Id,
                Name = c.Name
                FullAddress = c.FormatFullAddress(),
                Orders = c.Orders,
                // other properties (snip)
            }).SingleOrDefault();
}

This is using the LINQ provider built into NHibernate 3.0, if you're not using NHibernate 3.0, and can do report queries using Projections. The syntax escapes me so try here http://nhforge.org/doc/nh/en/index.html#querycriteria-projection

Sunday Ironfoot
I also vote for lazy loading in this case. Do you have some reason for not using it?
Petr Kozelek
Ok - I guess I should have explained why I want this.. I have a client and a server side. The server provides rest services such as GetAllCustomers and GetCustomerById. In some cases I want to list all Customers, but I don't want to get the complete Customer objects for the customers-list as this would be unnecessary load on the server. Therefore I only want to call GetAllCustomers - get the Customers without references - and then call GetCustomerById if I want a complete customer afterwards.
stiank81
Lazy Loading is perfect, and I would like to use it. Then I could explicitly initialize the Orders before I return it from the GetCustomerById and leave it uninitialized for GetAllCustomers. However, there are some problems with the webcommunication when using Lazy Loading, and therefore I'm trying to see if there are other ways to solve this. The communication problems are described in another question her: http://stackoverflow.com/questions/3776532/avoid-serializing-certain-properties-in-rest-services
stiank81
I'm not sure Nhibernate will maintain your Customer as an attached entity once it leaves it's own App Domain and travels over the wire. Once it reaches the other end, Customer would just become a detached POCO and Orders will return NULL (or an empty list). You may want to look at report queries, ie. simply returning a custom object for your web service.
Sunday Ironfoot
NHibernate wraps every object in something that will handle the lazyness. When you've exited the session scope it will be unable to fetch the objects, and hence fail. It won't simply be null. If that was the case it would be easy.
stiank81
@stiank81: But the thing that NHibernate wraps around your objects to handle laziness (it's called a dynamic proxy) will disappear once your object travels over the wire, it will just become a simple Customer object.
Sunday Ironfoot
Sure? Back when I was using SOAP I managed to serialize objects which still had pending lazy objects, but for some reason the serialization/deserialization/etc fails now that I'm using REST. Anyway; with SOAP I saw the objects back on the client side - with the "dynamic proxy" still present.
stiank81
@stiank81: Remember you're serialising your object to just XML text data, the dynamic proxy would require code and I'm pretty sure it's not possible to send C# code embedded in XML. Basically you can serialise and send data but not functionality. I'd imagine your SOAP example worked because both sides were using the same Assembly with the same class that had the neccessary code to perform the lazy loading. But how would a Java or Ruby on Rails client understand and execute your C# code. Think of SOAP and REST as sending 'messages'
Sunday Ironfoot
Yes, I did reference the same classes from both client and server when using SOAP - and I'm still doing this now with REST. I believe you are absolutely right in all your facts and assumptions here, but there are still problems with the communication regardless.
stiank81
Thx for you effort on this. +1! I will look into your edits.
stiank81
+1  A: 

You can specify the FetchMode in the Criteria:

var crit = session.CreateCriteria (typeof(SomeObject));
crit.SetFetchMode ("association", FetchMode.Eager);

You can also specify in the mapping that an association/collection should not be lazy loaded as well. The default is that collections are lazy loaded.

Frederik Gheysels
Thx. That is indeed useful to know! It doesn't solve my problem though as it is still wrapped with the lazyloading mechanism. What I'd like to do - if possible - is just to omit the references. Not lazy load them..
stiank81
Then, do not map the associations ?
Frederik Gheysels
Or, when you want to retrieve only 'a subset' of the data that is in your entity, you can create a 'DTO', anduse the AliasToBean Converter. NHibernate will be smart enough to only load the data that is required. (http://stackoverflow.com/questions/660820/using-unmapped-class-with-nhibernate-named-query/662725#662725)
Frederik Gheysels
Yeah - I need to map the associations on the main class, but it seems like creating a DTO might be the solution. Thx.
stiank81