The are several things that you need to fix:
1. Ask for an Expression
, not a Func
Your Find
method specifies that it wants a Func
. If you want an Expression
, you have to say so:
public IList<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
2. Use LINQ or QueryOver
instead of ICriteria
Assuming you're not using an ancient version of NHibernate that doesn't have LINQ or QueryOver (oddly, there are a LOT of people still using NHibernate 1.2.1 - I can't figure out why)...
ICriteria
doesn't understand Expression
s. Replace CreateCriteria(typeof(TEntity))
with Query<TEntity>()
or QueryOver<TEntity>()
depending on which query syntax you prefer.
3. Where
should go before Future
You need to move the Where
before the Future
. Future
is used to batch several queries together so that they all get executed in one round trip to the database as soon as your code tries to evaluate the results of one of the "Future" queries. At the point your above code calls Future
, the query only consists of CreateCriteria(typeof(TEntity))
, which just tells it which table to query against, like so:
select * from TEntity
If you want a where clause in there, you have to switch those method calls around:
filterCriterea.Where(predicate).Future<TEntity>()
This should give you a SQL query like:
select * from TEntity where ...
It is very important to learn the difference between IQueryable
and IEnumerable
. You should notice that Query
returns an IQueryable
, whereas Future
returns an IEnumerable
. Manipulating an IQueryable
using Expression
s will result in changes to the SQL that gets executed. Manipulating an IEnumerable
using a Func
just changes the way you're looking at the in-memory data.
4. ToList
immediately after Future
negates benefit of Future
ToList
iterates over the collection that is passed to it and puts each element in a list. The act of iterating over the collection will cause the Future query to be immediately executed, not giving you any chance to batch it together with other queries. If you must have a list, you should just omit the Future
. I think however, that it would be a better idea to change your method to return an IEnumerable
, which would give you the ability to batch this query together with other queries. Like so...
public IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> predicate)
{
var query = _unitOfWork.CurrentSession.Query<TEntity>();
return query.Where(predicate).Future<TEntity>();
}
In order to take advantage of the query batching, you are probably going to have to rearrange the code that calls this Find
method. For example, instead of...
foreach (var openThing in thingRepository.Find(x => x.Status == Status.Open))
CloseIt(openThing);
foreach (var negativeWhatsit in whatsitRepository.Find(x => x.Amount < 0))
BePositive(negativeWhatsit);
... you should do this:
var openThings = thingRepository.Find(x => x.Status == Status.Open);
var negativeWhatsits = whatsitRepository.Find(x => x.Amount < 0);
// both queries will be executed here in one round-trip to database
foreach (var openThing in openThings)
CloseIt(openThing);
foreach (var negativeWhatsit in negativeWhatsits)
BePositive(negativeWhatsit);