Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Fluent NHibernate does not create IN part of WHERE clause

I have Fluent NHibernate Linq queries where I check values based on run time arrays. A basic example would be something like:

var array = [1,2,3,4,5,6];
using (var session = SessionProvider.SessionFactory.OpenSession())
{
  return session.Query<MyObject>().Where(x => array.Contains(x.CompareVal)).ToList();
}

I would expect the generated SQL statement to look something like this:

SELECT CompareVal, Column1, Column2
FROM MyObject
WHERE CompareVal IN (1,2,3,4,5,6)

However, what I'm finding instead is that the generated SQL statement simply emits the WHERE clause (proven by watching in Profiler) and selects the entire table, and then seems to run the filter in memory once it gets all the data back.

Something to note - I have a Generic Repository class that all of these calls are funneled through. The Query method is as follows:

public IList<T> Query(Func<T, bool> criteria)
{
  using (var session = SessionProvider.SessionFactory.OpenSession())
  {
    return session.Query<T>().Where(criteria).ToList();
  }
}

Obviously this (lack of a where clause) is not acceptable in a table with a large amount of data. What can I do to force NHibernate to generate the query correctly with the WHERE clause and still keep a generic pattern for repositories?

like image 280
BlackSpy Avatar asked Feb 01 '13 13:02

BlackSpy


2 Answers

Does it make a difference if you change your Query method to the following ?

public IList<T> Query(Expression<Func<T, bool>> criteria)
{
  using (var session = SessionProvider.SessionFactory.OpenSession())
  {
    return session.Query<T>().Where(criteria).ToList();
  }
}

This is how I usually proceed with a generic Query :

    public List<TOut> GetEntitiesLinq<TIn,TOut>(Expression<Func<IQueryable<TIn>,IQueryable<TOut>>> myFunc)
    {
        var t = (myFunc.Compile())(_session.Query<TIn>()) ;
        return t.ToList();
    }

Then how I would use it in your case :

var myObjList = myQueryManager.GetEntitiesLinq<MyObject,MyObject>(x=>x.Where(myObj => array.Contains(myObj.CompareVal)));

Hope this will help

like image 84
jbl Avatar answered Oct 23 '22 06:10

jbl


Use Any:

 return session.Query<MyObject>().Where(x => array.Any(y => y == x.CompareVal)).ToList();

Your repository pattern (using plain Func) automatically materializes your query to list, if you want something to be deferredly executed, use IQueryable, don't use Func only

Something to note - I have a Generic Repository class that all of these calls are funneled through. The Query method is as follows:

public IList<T> Query(Func<T, bool> criteria)
{
  using (var session = SessionProvider.SessionFactory.OpenSession())
  {
    return session.Query<T>().Where(criteria).ToList();
  }
}

Your repository just mimic what is already provided out of the box by NHibernate

like image 39
Michael Buen Avatar answered Oct 23 '22 07:10

Michael Buen