views:

89

answers:

4

I am trying to set up proper domain architecture using Fluent NHibernate and Linq to NHibernate. I have my controllers calling my Repository classes, which do the NHibernate thang under the hood and pass back ICollections of data. This seems to work well because it abstracts the data access and keeps the NHibernate functionality in the "fine print".

However, now I'm finding situations where my controllers need to use the same data calls in a different context. For example, my repo returns a list of Users. That's great when I want to display a list of users, but when I want to start utilizing the child classes to show roles, etc., I run into SELECT N+1 issues. I know how to change that in NHibernate so it uses joins instead, but my specific question is WHERE do I put this logic? I don't want every GetAllUsers() call to return the roles also, but I do want some of them to.

So here are my three options that I see:

  1. Change the setting in my mapping so the roles are joined to my query.
  2. Create two Repository calls - GetAllUsers() and GetUsersAndRoles().
  3. Return my IQueryable object from the Repository to the Controller and use the NHibernate Expand method.

Sorry if I didn't explain this very well. I'm just jumping into DDD and a lot of this terminology is still new to me. Thanks!

A: 

I try and keep all the query logic in my repositories and try to only pass back the ICollection from them.

In your situation, I'd pass in some parameters to determine if you want to eager load roles or not and construct the IQueryable that way. For example:

GetAllUsers(bool loadRoles)
{
    var query = session.Linq<Users>();
    if(loadRoles)
       query.Expand("Roles");
    return query.ToList();
}
lomaxx
I like this answer, but I can really see it getting crazy in a complicated situation. My scenario was very simplified - my real situation has many child objects.
Mike C.
A: 

I would choose 2, creating two repositories. And perhaps would I consider creating another repository call to GetRoleByUser(User user). So, you could access a user's role upon user selection change on a seperate thread, if required, so it would increment your performance and won't load every user's roles for each of your users, which would require most resources.

Will Marcouiller
A: 

It sounds like you are asking if it is possible to make GetAllUsers() sometimes return just the Users entities and sometimes return the Users and the roles.

I would either make a separate repository method called GetRolesForUser(User user), use lazy loading for Roles, or use the GetAllUsers(bool loadRoles) mentioned by lomaxx's answer.

I would lean toward lazy loading roles or a separate method in your repository.

Adam
+2  A: 

As lomaxx points out, you need query.Expand.

To prevent your repository from becoming obscured with all kinds of methods for every possible situation, you could create Query Objects which make configurable queries.

I posted some examples using the ICriteria API on my blog. The ICriteria API has FetchMode instead of Expand, but the idea is the same.

Jan Willem B
I love this answer, thanks! Now I need to convert that to Linq...
Mike C.