Search code examples
c#.netnhibernatequeryovernhibernate-criteria

QueryOver: select ... where property in (...)


I try to use queryover to represent the following sql:

select * from Table1 t1 where t1.foreign_key in (select t2.Id from Table2 t2 where (...))

So I created a subquery for the inner select statement like this:

 var sq = QueryOver.Of<Table2>().Where(...).Select(c => c.Id);

However when I cannot use this subquery in the following query:

var query = QueryOver.Of<Table1>().WithSubquery.
 WhereProperty(t1 = t1.foreign_key).In(contactSubQuery);

I think the problem is that QueryOver expects a subquery over Table1 instead of Table2 in contactSubQuery, but then I cannot access the required properties of Table2. In How do I express a query containing a WHERE..IN subquery using NHibernate's QueryOver API? a similar problem is addressed (using JoinAlias), but I cannot see how to apply that solution for my case. Thanks for any help!

SOLUTION:

Thanks alot @Radim, you were almost right. I was already using

Queryover.Of<T>() 

in the query but the problem was that I was assigning it to a IQueryOver variable (since we have a no var-keyword styleguide in our company). After I assigned it to var it compiled. Since I did not expect this to cause the problem at all I simplified every variable to var in the question, so the posted code should actually already have worked lol... I checked the type and simply changed the query to (in accordance with the no-var rule):

QueryOver<Table1> = QueryOver.Of<Table1>()
        .WithSubquery
           .WhereProperty(t1 => t1.foreign_key)
           // won't compile, because passed is IQueryOver<T,T>, 
           // not the QueryOver<U>   
           .In(subquery)

where before I had...

IQueryOver<Table1, Table1> = ...

Again, thanks alot for the help!


Solution

  • You are almost there, just the syntax is not like this:

    var query = QueryOver.Of<Table1>().WithSubquery.
        WhereProperty(t1 = t1.foreign_key).IsIn(contactSubQuery);
    

    but:

     // subquery "sq"
     var sq = QueryOver.Of<Table2>().Where(...).Select(c => c.Id); 
     var query = QueryOver.Of<Table1>()
                .WithSubquery
                   .WhereProperty(t1 => t1.foreign_key)
                   .In(sq) // instead of .IsIn(contactSubQuery)
                ...
    

    Because .IsIn() is a general extension method:

    /// <summary>
    /// Apply an "in" constraint to the named property
    ///             Note: throws an exception outside of a QueryOver expression
    /// 
    /// </summary>
    public static bool IsIn(this object projection, ICollection values);
    

    while .In() is the method of the returned result "QueryOverSubqueryPropertyBuilderBase" (the result of the .WhereProperty() call)

    Also, be sure, that the passed argument into .In(subquery) is a QueryOver.Of<T>(). For example this is wrong:

    var subquery = session.QueryOver<T>(); // it is named subquery
    // but it is not of a type QueryOver<T>, but of a type
    // IQueryOver<T, T>
    // which is not what is expected here
    
     var query = QueryOver.Of<Table1>()
                .WithSubquery
                   .WhereProperty(t1 => t1.foreign_key)
                   // won't compile, because passed is IQueryOver<T,T>, 
                   // not the QueryOver<U>   
                   .In(subquery)
                ...
    

    And that will produce the:

    Error 1 The type arguments for method 'NHibernate.Criterion.Lambda.QueryOverSubqueryBuilderBase<NHibernate.IQueryOver<>.... cannot be inferred from the usage. Try specifying the type arguments explicitly.