linqnhibernatelinq-to-nhibernate

NHibernate IQueryable doesn't seem to delay execution


I'm using NHibernate 3.2 and I have a repository method that looks like:

    public IEnumerable<MyModel> GetActiveMyModel()
    {
        return from m in Session.Query<MyModel>()
               where m.Active == true
               select m;
    }

Which works as expected. However, sometimes when I use this method I want to filter it further:

    var models = MyRepository.GetActiveMyModel();
    var filtered = from m in models
                   where m.ID < 100
                   select new { m.Name };

Which produces the same SQL as the first one and the second filter and select must be done after the fact. I thought the whole point in LINQ is that it formed an expression tree that was unravelled when it's needed and therefore the correct SQL for the job could be created, saving my database requests.

If not, it means all of my repository methods have to return exactly what is needed and I can't make use of LINQ further down the chain without taking a penalty.

Have I got this wrong?

Updated

In response to the comment below: I omitted the line where I iterate over the results, which causes the initial SQL to be run (WHERE Active = 1) and the second filter (ID < 100) is obviously done in .NET.

Also, If I replace the second chunk of code with

var models = MyRepository.GetActiveMyModel();
var filtered = from m in models
               where m.Items.Count > 0
               select new { m.Name };

It generates the initial SQL to retrieve the active records and then runs a separate SQL statement for each record to find out how many Items it has, rather than writing something like I'd expect:

SELECT Name 
FROM MyModel m 
WHERE Active = 1 
    AND (SELECT COUNT(*) FROM Items WHERE MyModelID = m.ID) > 0

Solution

  • You are returning IEnumerable<MyModel> from the method, which will cause in-memory evaluation from that point on, even if the underlying sequence is IQueryable<MyModel>.

    If you want to allow code after GetActiveMyModel to add to the SQL query, return IQueryable<MyModel> instead.