+1  A: 

Perhaps you should give Nhibernate Linq a try. It allows you use the IQueryable and do things like:

Session.Linq<Book>().Where(b => b.Name == "The Great Gatsby");
Jonny Cundall
"I have looked at NHibernates Linq provider and currently I am making the decision to take large collections and split them into their own repository so that I can make explicit calls for filtering and paging."
Khalid Abuhakmeh
@Khalid - I don't know why this doesn't help you. It's exactly what you asked for
burnt_hand
My question was "How do I get NHibernate to map to an IQueryable property?" I already know about NHibernate Linq. This is an answer to a question I never asked and that is why I down voted it.
Khalid Abuhakmeh
+2  A: 

I tend to think like this:

Aggregate roots are boundaries of consistency, so if shelf needs to enforce some sort of consistency policies on the books it contains, then it should be an aggregate root. And in such case it should hold a set/collection of books.

If you don't need to enforce consistency in any way from shelf to books, then I'd consider to remove the set/collection property and move those queries into a repository instead.

Also, since pagination and filtering most likely don't have anything to do with your domain logic, it is most likely for presentation. Then I'd consider to make some special view for it instead of adding presentation facillities to my repositories.

e.g.

var result = Queries.FindBooksByShelf(shelfId,pageSize);

Such query could return projections and/or be optimized as plain SQL etc. They are most likely specific for a certain view or report in your GUI. This way your domain will focus on domain concepts only.

Roger Alsing
+3  A: 

I've been trying to come up with a solution for a similar problem.

You can filter collections off an entity using ISession.FilterCollection. This creates an additional IQuery where you can count, page, add criteria, etc.

So, for example (my query in FilterCollection may be a little off, but you should get the idea):

ISession session = GetSession();
var shelf = session.Get<Shelf>(id);
var books = session.FilterCollection(shelf.Books, "where Name = :title").SetString("title", "The Great Gatsby").List<Book>();

There are a problem with that, however:

  1. The consumer executing the code needs to access ISession.CreateFilter, or you need to create a method on your repository that takes in a property, a query, and your query arguments (as well as any paging or other information). Not really the sexiest thing on the planet.
  2. It's not the LINQ you wanted.

Unfortunately, I don't think there's any way to get what you want out of the box with NHibernate. You could fake it, if you wanted to try, but they seem to fall flat to me:

Add a method or property that under the covers returns a LINQ to NHibernate IQueryable for this shelf:

public IQueryable<Book> FindBooks() {
  return Resolver.Get<ISession>().Linq<Book>().Where(b => b.Shelf == this);
}

where someone might consume that like this:

var shelf = ShelfRepo.Get(id);
var books = (from book shelf.FindBooks()
             where book.Title == "The Great Gatsby"
             select book);

Yuck! You are bleeding your persistence needs through your domain model! Maybe you could make it a little less worse by having a repository emit IQueryable, which at runtime is actually LINQ to NHibernate:

public IQueryable<Book> FindBooks() {
  return Resolver.Get<IRepository<Book>>().CreateQuery().Where(b => b.Shelf == this);
}

Still pretty blah.

Create your own custom collection type (and potentially an IQueryable implementation) that wraps a private field of the actual books, and map NHibernate to that field. However, it may be a difficult undertaking getting that working with ISession.CreateFilter. You have to consider "discovering" the current session, converting the LINQ expression into something you can use in CreateFilter, etc. Plus, your business logic is still dependent on NHibernate.

Nothing really satisfies at this point. Until NHibernate can do LINQ over a collection for you, it appears that you're better off just querying your Book repository normally as has already been suggested, even if it doesn't seem as sexy or optimal.

HackedByChinese
Thank you for being so thorough in your explanation. I have thought about the things you've said, and I agree with all the points you make. Wonder if NHibernate 3 is just going to support this out of the box.
Khalid Abuhakmeh