了解外键not-null = true和与NHibernate处于零对一关系的逆行为

问题描述

| 我正在尝试让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\'
没有更新查询!     

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...