理解外键not null=与NHibernate的零对一关系中的正确和相反行为
我试图让NHibernate使用集合的多个方面来管理双向关联,以模拟零对一关系 父类和映射:理解外键not null=与NHibernate的零对一关系中的正确和相反行为,nhibernate,nhibernate-mapping,Nhibernate,Nhibernate Mapping,我试图让NHibernate使用集合的多个方面来管理双向关联,以模拟零对一关系 父类和映射: public class Parent { private ICollection<Child> children; public Parent() { this.children = new HashedSet<Child>(); } public virtual Guid Id { get; protected inte
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();
}
}
请注意,对于第二次插入和以下更新,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',N'@p0 uniqueidentifier,@p1 uniqueidentifier',@p0='AA5A146E-E3F5-4373-B7A8-9EF301171401',@p1='B78C4461-A217-47FC-BE02-9EF30117140A'
go
当此代码生成臭名昭著的not null属性时,会引用null或瞬时值反转
:
var parent = new Parent();
var child = new Child();
parent.Child = child;
//child.Parent = parent;
session.Save(parent);
session.Flush();
我已经找到了很多关于这方面的帖子,但是还没有找到一个关于如何做零对一的明确指南,在one
侧有inverse=false
我试过上面提到的一对多/一对一的方法
此外,我在NHibernate上发现了几个关于(不)可空外键的开放问题:,等等
我做错了什么
编辑2011-05-30
因此,我的临时解决方案是在多方面使用标准的inverse=true
设置,并在父级的setter中使用一些魔法:
public virtual Child Child
{
get { return children.FirstOrDefault(); }
set
{
{
this.children.Clear();
if (value != null)
{
value.Parent = this;
this.children.Add(value);
}
}
}
}
但我仍然对inverse=false
行为感到困惑,这在多对一方面应该相当于inverse=true
(有趣的是,FluentNhibernate不允许manytonepart像文章建议的那样设置inverse=true
。何时使用inverse=“true | false”
inverse
属性用于帮助NHibernate知道应该使用关系的哪一侧来保持关系。非反向侧(请注意双负)是将被持久化的侧。如果任何一方都不是相反的,那么关系将保持两次,就像前面提供的INSERT
之后紧接着是UPDATE
示例一样。如果双方都是反向的,那么关系就根本不会持久,因此正确设置反向是很重要的
我喜欢用下面的方式来思考逆
。我不知道这是否是正式的工作方式,但它帮助我的世界变得有意义:
多对一
多对一
关系总是inverse=“false”
。它们始终用于保持与数据库的关系。因为它们总是inverse=“false”
,所以不需要指定它,所以NHibernate(因此Fluent NHibernate)不提供选项
(我只遇到过一种情况,希望能够指定inverse=“true”多对一
上的
。如果一方是一对多
列表,另一方是多对一
,您应该能够让列表
控制关系,以便NHibernate可以为您设置索引
值。目前,您有向子类添加属性并自己管理索引值。)
一对一
一对一
关系始终是inverse=“true”
。在关系的另一端,如果没有id
或多对一
,它们就永远不会存在,这将负责保持关系。由于逆
值始终相同,因此不需要指定它,因此不支持指定它
收藏
像bag
、list
、set
等集合可能是也可能不是双向关系的一部分。如果它们单独存在(可能是一包字符串元素),那么它们需要是inverse=“false”
(这是默认值),因为没有其他人负责保持关系。但是,如果它们与其他关系同时存在(就像传统的一对多/多对一),则应将它们指定为inverse=“true”
使用“多对多”
集合,在关系的任意一侧都有一个集合,将其中一个标记为reverse=“true”
,将另一个保留为默认值reverse=“false”
。同样,关键是关系的一侧必须是非反向的。你应该选择哪一边?如果我们以用户和角色之间的多对多关系为例,您可能有很多用户和一些角色。在我看来,您应该将Role.Users
映射为inverse=“true”
,并让User.Roles
控制关系,因为它是一组较小的数据,而且可能是您更关心的集合
(事实上,我会犹豫是否将Role.Users
包含在模型中。假设一个“客户”角色有100000个用户。那么customerRole.Users
是一个无法使用的延迟加载炸弹,等待爆炸。)
…回到你的问题上。。。
因为关系的哪一方是相反的并不重要,只要其中一方是非相反的,那么你就应该让一对一的一方成为相反的一方,因为这是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();
}
}
没有更新查询 相关答案(由我:-)如果您有一个中间表,其中包含两个实体之间的关系,那么多对多呢?@Dark_Knight多对多
在上面的“集合”部分的第2段中介绍。
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();
}
}
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'