问题描述
|
我正在尝试让NHibernate使用集合的多个方面来管理双向关联以对零对一关系进行建模。
家长班和地图:
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();
}
}
子班和地图:
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();
}
}
以下代码产生两个插入和一个更新:
var parent = new Parent();
var child = new Child();
parent.Child = child;
child.Parent = parent;
session.Save(parent);
session.Flush();
注意第二次插入和以下更新的本质上重复的SQL:
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\',@p1=\'B78C4461-A217-47FC-BE02-9EF30117140A\'
go
虽然这段代码产生了臭名昭著的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();
我已经找到了很多与此相关的文章,但是还没有找到有关如何进行零对一处理的权威指南,其中ѭ6表示one
。
我尝试了这里提到的一对多/一对一方法。
同样,我在NHibernate上发现了一些关于(不是)可空外键的未解决问题:NH-941,NH-1050等。
我究竟做错了什么?
编辑2011-05-30
因此,我的临时解决方案是在许多方面采用标准的“ 8”设置,并在“父级”的二传手中做一些魔术:
public virtual Child Child
{
get { return children.FirstOrDefault(); }
set
{
{
this.children.Clear();
if (value != null)
{
value.Parent = this;
this.children.Add(value);
}
}
}
}
但是我仍然对ѭ6行为感到困惑,这在多对一方面应该等效于ѭ8(有趣的是,FluentNhibernate不允许如本文所建议的那样将ManyToOnePart设置为ѭ8)。
解决方法
何时使用inverse = \“ true | false \”
inverse
属性用于帮助NHibernate知道应该使用关系的哪一边来保留该关系。非反面(请注意双负数)是将保留的面。如果双方都不相反,则该关系将被保留两次,就像INSERT
之后紧跟着上面提供的UPDATE
示例。如果双方都是相反的,则该关系将根本不会保留,因此正确设置反向很重要。
我喜欢以以下方式考虑inverse
。我不知道这是否正式是它的工作方式,但它可以帮助我的世界理解:
多对一
many-to-one
的关系总是always18ѭ。它们始终用于保持与数据库的关系。由于它们始终是inverse=\"false\"
,因此无需指定它,因此NHibernate(因此也只有Fluent NHibernate)没有为其提供选项。
(我只遇到一种情况,希望我可以在ѭ17上指定inverse=\"true\"
。如果一侧有one-to-many
list
,而另一侧有many-to-one
,则应该让list
控制关系,以便NHibernate可以为您设置index
值。按照目前的情况,您必须向子类添加一个属性并自己管理index
值。)
一对一
one-to-one
的关系总是inverse=\"true\"
。在关系的另一边,如果没有ѭ30或many-to-one
,它们就永远不会存在,它们会照顾到保持关系。由于inverse
值始终是相同的,因此无需指定它,因此不支持指定它。
馆藏
bag
,list
,set
等集合可能是也可能不是双向关系的一部分。如果它们独立存在(也许是一串字符串元素),则它们必须为inverse=\"false\"
(默认值),因为没有其他人将负责持久化该关系。但是,如果它们与另一种关系一起存在(例如,传统的一对多/多对一),则应将它们指定为inverse=\"true\"
。
对于many-to-many
集合,如果您在关系的任一侧都有一个集合,则将其中一个标记为inverse=\"true\"
,将另一个标记为默认值inverse=\"false\"
。再次强调,关系的一侧必须是非反向的。您应该选择哪一边?例如,如果我们以“用户和角色”之间的多对多关系为例,您可能会有很多用户和几个角色。在我看来,您应该将Role.Users
映射为ѭ20let,并让ѭ43the控制这种关系,因为这是处理的较小数据集,并且无论如何它可能是您更关心的集合。
(实际上,我很犹豫在模型中完全包括“ 41”。假设“客户”角色有100,000个用户。那么,“ 45”是等待爆炸的无法使用的惰性加载炸弹。)
...回到您的问题...
由于关系的哪一侧是相反的并不重要,只要一侧是非相反的,则应将one-to-one
一侧设为相反的一侧,因为NHibernate就是这样做的。不要将工具争夺无关紧要的东西。在您提供的映射中,基本上关系的两面都被标记为非逆的,这导致该关系被保留两次。以下映射应该更适合您:
public class Parent
{
public virtual Guid Id { get; set; }
public virtual Child Child { get; set; }
}
public class ParentClassMap : ClassMap<Parent>
{
public ParentClassMap()
{
Id(x => x.Id);
HasOne(x => x.Child)
.PropertyRef(x => x.Parent)
.Cascade.All();
}
}
public class Child
{
public virtual Guid Id { get; set; }
public virtual Parent Parent { get; set; }
}
public class ChildClassMap : ClassMap<Child>
{
public ChildClassMap()
{
Id(x => x.Id);
References(x => x.Parent)
.Not.Nullable()
.Unique()
.Cascade.SaveUpdate();
}
}
...这会从您的测试插入代码中产生以下SQL:
exec sp_executesql N\'INSERT INTO [Parent] (Id) VALUES (@p0)\',N\'@p0 uniqueidentifier\',@p0=\'925237BE-558B-4985-BDA2-9F36000797F5\'
exec sp_executesql N\'INSERT INTO [Child] (Parent_id,Id) VALUES (@p0,@p1)\',N\'@p0 uniqueidentifier,@p1 uniqueidentifier\',@p0=\'925237BE-558B-4985-BDA2-9F36000797F5\',@p1=\'BE6D931A-8A05-4662-B5CD-9F36000797FF\'
没有更新查询!