如何将新对象添加到IList映射为NHIBnate的一对多?

时间:2009-08-10 07:06:55

标签: nhibernate fluent-nhibernate one-to-many

我的模型包含一个类Section,它有一个Statics的有序列表,属于本节的一部分。抛弃所有其他属性,模型的实现如下所示:

public class Section
{
    public virtual int Id { get; private set; }
    public virtual IList<Static> Statics { get; private set; }
}

public class Static
{
    public virtual int Id { get; private set; }
}

在数据库中,关系实现为一对多,其中表Static具有指向Section的外键和用于存储其的整数列Position列表中的索引位置是它的一部分。

映射在Fluent NHibernate中完成,如下所示:

public SectionMap()
{
    Id(x => x.Id);
    HasMany(x => x.Statics).Cascade.All().LazyLoad()
            .AsList(x => x.WithColumn("Position"));
}

public StaticMap()
{
    Id(x => x.Id);
    References(x => x.Section);
}

现在我可以加载现有的Static,我也可以更新其中的详细信息。但是,我似乎找不到将Static添加到Section的方法,并将此更改保留到数据库中。我尝试了几种组合:

  • mySection.Statics.Add(myStatic)
  • session.Update(mySection)
  • session.Save(myStatic)

但是我得到的最接近的(使用前两个语句)是一个SQL异常读取:“不能将值NULL插入列'Position'”。显然,这里尝试INSERT,但NHibernate似乎没有自动将索引位置附加到SQL语句。

我做错了什么?我在映射中遗漏了什么吗?我是否需要将Position列公开为属性并自行为其分配值?

编辑:如果删除数据库中NOT NULL列的Static.Position约束,显然一切都按预期工作。我想NHibernate在用Position值更新行后立即生成插入。

虽然这是问题的答案,但我不确定它是否是最好的。我希望Position列不可为空,所以我仍然希望有一些方法可以让NHibernate直接在INSERT语句中为该列提供值。

因此,问题仍然存在。还有其他解决方案吗?

3 个答案:

答案 0 :(得分:7)

在NHibernate中使用双向一对多关系时,其中一个端点必须是“反向”。最佳做法是将集合的结尾设置为反向,因为这样可以避免不必要的SQL语句,并允许id列为“非空”。

section 6.4文档中,您可以找到以下注释:

  

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

因此,您需要将.Inverse()添加到SectionMap中的HasMany映射。

public SectionMap()
{
    Id(x => x.Id);
    HasMany(x => x.Statics)
        .Cascade.All()
        .LazyLoad()
        .Inverse()
        .AsList(x => x.WithColumn("Position"));
}

您可能还需要在Section上添加和删除方法,该方法设置/重置静态引用以及向其自身集合添加/删除静态:

public virtual void AddStatic(Static static)
{
    Statics.Add(static);
    static.Section = this;
}


public virtual void RemoveStatic(Static static)
{
    Statics.Remove(static);
    static.Section = null;
}

这些方法可确保关系两边的参考文献保持准确。

根据文档的section 6.8,NHibernate在使用索引集合时不支持双向关系:

  

请注意,NHibernate不支持与索引集合(列表,映射或数组)的双向一对多关联作为“多”端,您必须使用集或包映射。

因此,如果您仍然遇到问题,请考虑使用单向关系而不是双向关系,但这可能意味着您的外键列需要可以为空(根据帖子开头的注释)。否则,您可能必须将集合映射为包或集而不是列表。

答案 1 :(得分:2)

在静态表中,您有一个名为“SectionID”的字段或类似的字段。 让这个字段为NULL:NHibernate首先添加新记录,然后更新引用的id。

我发现在我的数据库中我也很惊讶:为什么NH不喜欢数据库级别的正确表引用?

答案 2 :(得分:1)

我以为我会投入0.02美元,只是因为我来到这里寻找解决问题的方法,似乎没有任何答案能够实现。

你的解决方案几乎就在那里。您对HasMany的映射是正确的。正如你所说的那样,问题在于数据库中的位置没有得到更新(当你将它设置为null时,它只会“工作”,因为NULL会被插入到数据库中;对我来说这不是真的有效;) )。

我发现它有点奇怪,你需要在Static类上映射位置,显然在Static上包含一个Position属性。您不希望Position属性可写,因为它的值取决于Static在列表中的位置。

将以下内容添加到静态

public virtual int Position 
{
    get 
    {
        // Will throw exception if Section is null
        // or Section.Statics is null...
        return Section.Statics.IndexOf(this);
    }
    protected set 
    {
    }
}

在db中的Position列中将更新(记得在StaticMap中映射你的位置)。

我猜测静态的NHib代理可以更新位置字段,具体取决于它在列表中的位置(通过比我拥有的更大的魔法),然后保存到数据库。