I'm trying to get NHibernate to use the many side of a collection to manage a bidirectional association to model a zero-to-one relationship.
Parent Class and Map:
public class Parent
{
private ICollection<Child> children;
public Parent()
{
this.children = new HashedSet<Child>();
}
public virtual Guid Id { get; protected internal set; }
public virtual Child Child
{
get { return children.FirstOrDefault(); }
set
{
{
this.children.Clear();
if (value != null)
{
this.children.Add(value);
}
}
}
}
}
public class ParentMap : ClassMap<Parent>
{
public ParentMap()
{
this.Id(x => x.Id)
.GeneratedBy.GuidComb();
this.HasMany<Child>(Reveal.Member<Parent>("children"))
.Access.Field()
.Cascade.All()
.Not.Inverse()
.AsSet();
}
}
Child Class and Map:
public class Child
{
public virtual Guid Id { get; protected internal set; }
public virtual Parent Parent { get; set; }
}
public class ChildMap : ClassMap<Child>
{
public ChildMap()
{
this.Id(x => x.Id)
.GeneratedBy.GuidComb();
this.References(x => x.Parent)
.Not.Nullable()
.Cascade.All();
}
}
The following code produces two inserts and an update:
var parent = new Parent();
var child = new Child();
parent.Child = child;
child.Parent = parent;
session.Save(parent);
session.Flush();
Notice the essentially duplicate SQL for the second insert and the following update:
exec sp_executesql N'INSERT INTO [Parent] (Id) VALUES (@p0)',N'@p0 uniqueidentifier',@p0='AA5A146E-E3F5-4373-B7A8-9EF301171401'
go
exec sp_executesql N'INSERT INTO [Child] (Parent_id, Id) VALUES (@p0, @p1)',N'@p0 uniqueidentifier,@p1 uniqueidentifier',@p0='AA5A146E-E3F5-4373-B7A8-9EF301171401',@p1='B78C4461-A217-47FC-BE02-9EF30117140A'
go
exec sp_executesql N'UPDATE [Child] SET Parent_id = @p0 WHERE Id = @p1',N'@p0 uniqueidentifier,@p1 uniqueidentifier',@p0='AA5A146E-E3F5-4373-B7A8-9EF301171401',@p1='B78C4461-A217-47FC-BE02-9EF30117140A'
go
While this code produces the infamous not-null property references a null or transient value inverse
:
var parent = new Parent();
var child = new Child();
parent.Child = child;
//child.Parent = parent;
session.Save(parent);
session.Flush();
I've found numerous posts about this, but have yet to find a definitive guide on how to do zero-to-one, with inverse=false
on the one
side.
I've tried the one-to-many/one-to-one method mentioned here.
As well, I've found several open issues on NHibernate about (not)nullable Foreign Keys: NH-941, NH-1050, etc..
What am I doing wrong?
Edit 2011-05-30
So, my temporary solution is to go for the standard inverse=true
setting on the many side, and do some magic in the setter of the Parent:
public virtual Child Child
{
get { return children.FirstOrDefault(); }
set
{
{
this.children.Clear();
if (value != null)
{
value.Parent = this;
this.children.Add(value);
}
}
}
}
But I'm still baffled by the inverse=false
behavior, which should be the equivalent of inverse=true
on the many-to-one side (interestingly, FluentNhibernate doesn't allow for the ManyToOnePart to set inverse=true
like this article recommends).