单向一对多关系的NHibernate配置

单向一对多关系的NHibernate配置,nhibernate,fluent-nhibernate,one-to-many,Nhibernate,Fluent Nhibernate,One To Many,我正试图建立一种如下的关系。每个主项都有一个或多个细节项: public class Detail { public virtual Guid DetailId { get; set; } public virtual string Name { get; set; } } public class Master { public virtual Guid MasterId { get; set; } public virtual string Name { ge

我正试图建立一种如下的关系。每个主项都有一个或多个细节项:

public class Detail {
    public virtual Guid DetailId { get; set; }
    public virtual string Name { get; set; }
}
public class Master {
    public virtual Guid MasterId { get; set; }
    public virtual string Name { get; set; }
    public virtual IList<Detail> Details { get; set; }
}
详细信息是:

DetailId   uniqueidentifier NOT NULL
name       nvarchar(max) NULL
MasterId   uniqueidentifier NULL
foreign key (masterId) references [Master]
我真的不想有一个从细节到主元素的链接——换句话说,细节对象本身对我的域层不感兴趣。它们将始终通过其主对象进行访问

使用如下代码:

Master mast = new Master 
{
    MasterId = new Guid(),
    Name = "test",
    Details = new List<Detail> 
    {
        new Detail { .DetailId = new Guid(), .Name = "Test1" },
        new Detail { .DetailId = new Guid(), .Name = "Test1" }
    }
};

using (transaction == Session.BeginTransaction) 
{
    Session.Save(mast);
    transaction.Commit();
}
主桅杆=新主桅杆
{
MasterId=新Guid(),
Name=“test”,
详细信息=新列表
{
新详细信息{.DetailId=new Guid(),.Name=“Test1”},
新详细信息{.DetailId=new Guid(),.Name=“Test1”}
}
};
使用(事务==Session.BeginTransaction)
{
会议.拯救(桅杆);
Commit();
}
这非常有效,除了中列出的疯狂限制:NHibernate先插入Detail.MasterId并将其设置为NULL,然后进行更新以将其设置为真正的MasterId

实际上,我不希望细节条目的MasterId为NULL,因此如果我将MasterId字段设置为notnull,那么插入细节将失败,因为正如我所说的,NHibernate正在尝试将MasterId=NULL输入

我想我的问题可以归结为:

如何使上述代码示例与我现有的域模型(例如,不添加Detail.Master属性)和数据库中的Detail.MasterId字段一起工作


有没有办法让Nhibernate只在初始插入中放入正确的MasterId,而不是在之后运行更新?这个设计决策有什么理由吗?--我很难理解为什么会这样做。

你不能。要引用您链接到的其他问题的链接,请执行以下操作:

非常重要的注意事项:如果
关联的
列声明为非空,则NHibernate在创建或更新关联时可能会导致违反约束。为防止此问题,必须使用双向关联,将多值端(集合或包)标记为
inverse=“true”
。请参阅本章后面关于双向关联的讨论

编辑:正如哈兹克正确指出的那样,这在NHibernate 3及以上版本中发生了变化。遗憾的是,这些文档尚未更新,因此哈兹克:

[如果您]在
上设置
inverse=“false”
not null
,NH3及以上版本将仅执行插入更新的两次插入


NHibernate这样做的原因是:
当它保存细节时,它只知道细节所知道的东西。因此,任何发生在后台的主引用都将被忽略。 只有在保存主控形状时,它才会看到关系并使用主控形状的id更新集合的元素。

这是从面向对象的角度来看的。然而,从节约的角度来看,这种做法的逻辑性稍差一些。我想你总是可以提交一份bug报告,或者看看它是否已经提交,然后要求他们更改它。但我认为它们有其特定的(设计/域)原因。

NH3及以上版本允许在单向一对多映射的情况下更正保存实体,而无需烦人的
save null
-
save
-
update
循环,前提是同时启用
not null=“true”
inverse=“false”

FluentNHibernate代码段:

public class MasterMap : ClassMap<Master> 
{
    public MasterMap() 
    {
        Id(x => x.MasterId);
        Map(x => x.Name);
        HasMany(x => x.Details)
            .Not.Inverse()     //these options are very
            .Not.KeyNullable() //important and work only if set together 
            .Not.KeyUpdate()   //to prevent double update
            .Cascade.All();
    }
}
公共类主地图:类地图
{
公共总图()
{
Id(x=>x.MasterId);
Map(x=>x.Name);
HasMany(x=>x.Details)
.Not.Inverse()//这些选项非常有用
.Not.KeyNullable()//只有在一起设置时才有效
.Not.KeyUpdate()//防止重复更新
.Cascade.All();
}
}

我确实读过,但它并没有真正解释任何事情(双向关联的讨论也没有提到)。这一决定背后是否有理由?这是nhibernate的设计限制吗?是虫子吗?作为一个对nhibernate比较陌生的人,而且根本不知道它的内部结构,看起来这应该是可能的……老实说,我不知道。NHibernate团队没有将它归类为bug,它只是NHibernate如何持久化实体的副作用。我不会假装知道原因,但我想这与数据库和身份生成器无关。实际上,如果在NH3和以上键上设置reverse=“false”和not null,则在插入insert update的insead中只执行两次插入我不知道为什么,但如果我设置.not.KeyNullable().not.reverse()insert按预期工作-设置外键,但稍后nhibernate会发出额外的更新,这是不需要的:设置已设置的外键。。。是这样设计的吗?+1 Vicciar,同样的问题。使用有效外键正确插入,但仍会进行更新。是否确定不会发出重复更新?我按照概述设置了映射,它在insert上正确地设置了外键(而不是插入null),但随后它会执行额外的数据库更新,将外键重置为它在insert中使用的相同值。看起来VikciaR正在经历,其他几个人(基于upvotes)也在经历同样的事情。查询按预期工作,但nhibernate仍在数据库上创建一列,允许使用可为空的值?这正常吗?
Master mast = new Master 
{
    MasterId = new Guid(),
    Name = "test",
    Details = new List<Detail> 
    {
        new Detail { .DetailId = new Guid(), .Name = "Test1" },
        new Detail { .DetailId = new Guid(), .Name = "Test1" }
    }
};

using (transaction == Session.BeginTransaction) 
{
    Session.Save(mast);
    transaction.Commit();
}
public class MasterMap : ClassMap<Master> 
{
    public MasterMap() 
    {
        Id(x => x.MasterId);
        Map(x => x.Name);
        HasMany(x => x.Details)
            .Not.Inverse()     //these options are very
            .Not.KeyNullable() //important and work only if set together 
            .Not.KeyUpdate()   //to prevent double update
            .Cascade.All();
    }
}