I have one-to-one relationship between two tables, and I want to be able to create a LINQ query that will return the "parent" tables where there is something in the child table. The problem is that the query NH is generating is checking to see if the parent table's ID is not null (and it never is) rather than joining to the child table. This is regardless of whether I use lazy or non-lazy loading. I'm using custom automap conventions with an override, but here's the HBM XML that gets generated:
Mapping for Abstract Class, Concrete Class that is Parent
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class xmlns="urn:nhibernate-mapping-2.2" name="AbstractClass, DomainObjects, Version=2.2.1.0, Culture=neutral, PublicKeyToken=aaaaaaaaaaaaaaaa" table="ABSTRACT_CLASS">
<id name="AbstractClassId" type="System.Nullable`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<column name="ABSTRACT_CLASS_ID" />
<generator class="identity" />
</id>
<joined-subclass name="ConcreteClass, DomainObjects, Version=2.2.1.0, Culture=neutral, PublicKeyToken=aaaaaaaaaaaaaaaa" table="CONCRETE_CLASS">
<key>
<column name="ABSTRACT_CLASS_ID" />
</key>
<one-to-one cascade="none" class="AuxiliaryClass, DomainObjects, Version=2.2.1.0, Culture=neutral, PublicKeyToken=aaaaaaaaaaaaaaaa" name="AuxiliaryClass" property-ref="Foo" />
</joined-subclass>
</class>
</hibernate-mapping>
Mapping for Child Table
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
<class xmlns="urn:nhibernate-mapping-2.2" name="AuxiliaryClass, DomainObjects, Version=2.2.1.0, Culture=neutral, PublicKeyToken=aaaaaaaaaaaaaaaa" table="AUXILIARY_CLASS">
<id name="AuxiliaryClassiD" type="System.Nullable`1[[System.Int32, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<column name="AUXILIARY_CLASS_ID" />
<generator class="identity" />
</id>
<many-to-one class="ConcreteClass, DomainObjects, Version=2.2.1.0, Culture=neutral, PublicKeyToken=aaaaaaaaaaaaaaaa" name="Foo">
<column name="FOO_ABSTRACT_CLASS_ID" />
</many-to-one>
</class>
</hibernate-mapping>
Class Definitions
public abstract class AbstractClass
{
public virtual Int32? AbstractClassId { get; set; }
}
public class ConcreteClass : AbstractClass
{
public virtual AuxiliaryClass AuxiliaryClass { get; set; }
}
public class AuxiliaryClass
{
public virtual Int32? AuxiliaryClassId { get; set; }
public virtual ConcreteClass Foo { get; set; }
}
The LINQ query that isn't work is:
nh.Query<ConcreteClass>().Where(cc => cc.AuxiliaryClass != null);
The query that's being generated is:
select
concretecl0_.CONCRETE_CLASS_ID as CONCRETE1_0_
from
CONCRETE_CLASS concretecl0_
inner join
ABSTRACT_CLASS concretecl0_1_
on
concretecl0_.ABSTRACT_CLASS_ID=concretecl0_1_.ABSTRACT_CLASS_ID
where
concretecl0_.ABSTRACT_CLASS_ID is not null
If I turn off lazy loading, the joins out to the auxiliary table but still compares the concrete class's table's ID to null.
edit
Per @Suhas's suggestion:
Try changing your Linq query to nh.Query(cc => cc.AuxiliaryClass.AuxiliaryClassId > 0); assuming AuxiliaryClassId is of type int
I actually did cc => cc.AuxiliaryClass.AuxiliaryClassId != null
, which worked, getting me this query:
select
concretecl0_.ABSTRACT_CLASS_ID as concretecl0_1_0_
from
CONCRETE_CLASS concretecl0_
inner join
ABSTRACT_CLASS concretecl0_1_
on
concretecl0_.concretecl0_=concretecl0_1_.concretecl0_
, AUXILIARY_CLASS auxiliaryc1_
where
concretecl0_.ABSTRACT_CLASS_ID=auxiliaryc1_.FOO_ABSTRACT_CLASS_ID
and (auxiliaryc1_.AUXILIARY_CLASS_ID is not null)
However, when I tried the inverse case, cc => cc.AuxiliaryClass.AuxiliaryClassId == null
, I got a non-working query:
select
concretecl0_.ABSTRACT_CLASS_ID as concretecl0_1_0_
from
CONCRETE_CLASS concretecl0_
inner join
ABSTRACT_CLASS concretecl0_1_
on
concretecl0_.concretecl0_=concretecl0_1_.concretecl0_
, AUXILIARY_CLASS auxiliaryc1_
where
concretecl0_.ABSTRACT_CLASS_ID=auxiliaryc1_.FOO_ABSTRACT_CLASS_ID
and (auxiliaryc1_.AUXILIARY_CLASS_ID is null)
Just listing down my comments (slightly tailored to an answer) from the original questions as those seems to have helped the author of the question
nh.Query<ConcreteClass>(cc => cc.AuxiliaryClass.AuxiliaryClassId > 0);
assumingAuxiliaryClassId
is of typeint
ConcreteClass
but not present inAuxiliaryClass
. For that you might want to use left outer join. If you do left outer join, then you can do it via QueryOver (or Linq in a not so direct way) and then do the check for nullity in your code. Depending on the size of the dataset you load, this may not be a good thing to do in production. I am not sure how would do subquery to achieve what you are trying to achieveDefaultIfEmpty
method to get left outer joins in LINQ. This SO question should be a good starting point.ISession.CreateSQLQuery
and use your SQL query as is