I have two NHibernate entities with a one-to-one relationship. Let's call them Dog and Owner.
public class Dog
{
public virtual int Id { get;set; }
public virtual Owner Owner { get; set; }
}
public class Owner
{
public virtual int Id { get; set; }
public virtual Dog Dog { get; set; }
}
And Dog may have one or zero owners. The Fluent/NHibernate mappings are as so:
public class DogMap : ClassMap<Dog>
{
public DogMap()
{
Table("Dogs");
Id(x => x.Id);
HasOne( x=> x.Owner)
.Fetch.Join()
.Not.LazyLoad();
}
}
public class OwnerMap : ClassMap<Owner>
{
public OwnerMap()
{
Table("Owners");
// Owners share the same primary-key as dogs
Id(x => x.Id).GeneratedBy.Foreign("Dog");
References( x => x.Dog)
.Unique()
.Not.LazyLoad();
}
}
Now I simply wish to select Dogs which don't have an Owner.
I assumed the query would be:
Owner owner = null;
var ownerlessDogs = session
.QueryOver<Dog>()
.Left.JoinAlias(x => x.Owner, () => owner)
// Restrict on alias
.WhereRestrictionOn(() => owner).IsNull
.List();
But as you may have guessed, this does not work. It throws 'Object reference not set to ...'.
If I try,
var ownerlessDogs = session
.QueryOver<Dog>()
.Left.JoinAlias(x => x.Owner, () => owner)
// Restrict on property of root object
.WhereRestrictionOn(x => x.Owner).IsNull
.List();
It generates basically generates
SELECT {{relevant columns}}
FROM Dogs dogs
LEFT OUTER JOIN OWNERS owners
WHERE dogs.Id IS NULL
Almost right, but it is filtering on the primary-key of dogs, rather than the dogs foreign-key on owners.
With some help from an old post in the Hibernate forum I found a solution:
public class DogMap : ClassMap<Dog> {
public DogMap() {
Table("Dogs");
Id(x => x.Id);
HasOne( x=> x.Owner)
.Fetch.Join();
}
}
public class OwnerMap : ClassMap<Owner> {
public OwnerMap() {
Table("Owners");
//owners share the same primary-key as dogs
Id(x => x.Id).GeneratedBy.Foreign("Dog");
//both sides should be mapped as HasOne
HasOne( x => x.Dog)
.Constrained()
.Fetch.Join();
}
}
And the working query is
var ownerlessDogs = session
.QueryOver<Dog>()
.Left.JoinAlias(x => x.Owner, () => owner)
//this is the trick, restrict on Id
.WhereRestrictionOn( x => x.Owner.Id ).IsNull
.List();