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

发布于 2024-08-01 20:32:08 字数 1605 浏览 9 评论 0原文

我的模型包含一个 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 有一个外键指向 < code>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 添加到 Section 中,并将此更改保留到数据库中。 我尝试了以下几种组合:

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

但我得到的最接近的结果(使用前两条语句)是 SQL 异常读取:“无法将 NULL 值插入列‘位置’”。 显然,这里尝试了 INSERT,但是 NHibernate 似乎没有自动将索引位置附加到 SQL 语句中。

我究竟做错了什么? 我的映射中是否遗漏了某些内容? 我是否需要将 Position 列公开为属性并自己为其分配值?

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

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

因此,这个问题仍然悬而未决。 还有其他解决方案吗?

My model contains a class Section which has an ordered list of Statics that are part of this section. Leaving all the other properties out, the implementation of the model looks like this:

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; }
}

In the database, the relationship is implemented as a one-to-many, where the table Static has a foreign key pointing to Section and an integer column Position to store its index position in the list it is part of.

The mapping is done in Fluent NHibernate like this:

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);
}

Now I am able to load existing Statics, and I am also able to update the details of those. However, I cannot seem to find a way to add new Statics to a Section, and have this change persisted to the database. I have tried several combinations of:

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

but the closest I have gotten (using the first two statements), is to an SQL exception reading: "Cannot insert the value NULL into column 'Position'". Clearly an INSERT is attempted here, but NHibernate does not seem to automatically append the index position to the SQL statement.

What am I doing wrong? Am I missing something in my mappings? Do I need to expose the Position column as a property and assign a value to it myself?

EDIT: Apparently everything works as expected, if I remove the NOT NULL constraint on the Static.Position column in the database. I guess NHibernate makes the insert and immediatly after updates the row with a Position value.

While this is an anwers to the question, I am not sure if it is the best one. I would prefer the Position column to be not nullable, so I still hope there is some way to make NHibernate provide a value for that column directly in the INSERT statement.

Thus, the question is still open. Any other solutions?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

扶醉桌前 2024-08-08 20:32:08

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

在文档的 第 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"));
}

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

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


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

这些方法确保引用在两侧都保持准确。的关系。

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

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

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

When using a bidirectional one-to-many relationship in NHibernate one of the ends must be "inverse". Best practice is to set the end with the collection as inverse, since that avoids unnecessary SQL statements and allows the id column to be "not null".

In section 6.4 of the documentation you can find the following note:

Very Important Note: If the column of a association is declared NOT NULL, NHibernate may cause constraint violations when it creates or updates the association. To prevent this problem, you must use a bidirectional association with the many valued end (the set or bag) marked as inverse="true". See the discussion of bidirectional associations later in this chapter.

So, you need to add .Inverse() to your HasMany mapping in SectionMap.

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

You would also probably want an Add and Remove method on Section, which sets/resets the reference of the static as well as adding/removing the static to/from its own collection:

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


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

These methods makes sure the references are kept accurate on both sides of the relationship.

According to section 6.8 of the docs NHibernate does not support bidirectional relationships when using indexed collections:

Please note that NHibernate does not support bidirectional one-to-many associations with an indexed collection (list, map or array) as the "many" end, you have to use a set or bag mapping.

So, if you are still having trouble, consider using a unidirectional relationship instead of a bidirectional, however that might mean that your foreign key column needs to be nullable (according to the note in the beginning of the post). Otherwise you might have to map your collection as a bag or set instead of a list.

稚气少女 2024-08-08 20:32:08

我想我应该投入 0.02 美元,只是因为我来这里是为了解决你的问题,但似乎没有一个答案真正有效。

您的解决方案即将完成。 您的 HasMany 映射是正确的。 问题是,正如您所说,位置不会在数据库中更新(FWIW,当您将其设置为 null 时,它只能“起作用”,因为 NULL 被插入到数据库中;对我来说,这实际上不起作用;) )。

我觉得有点奇怪,您也需要在 Static 类上映射位置,并且显然在 Static 上包含一个 Position 属性。 不过,您不希望 Position 属性可写,因为它的值由 Static 在列表中的位置决定。

将以下内容添加到 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 
    {
    }
}

数据库中的 Position 列中的内容将被更新(请记住在 StaticMap 中映射您的 Position)。

我猜测 Static 的 NHib 代理可以根据它在列表中的位置来更新 Position 字段(通过一些比我拥有的魔法更大的魔法),然后将其保存到数据库中。

I thought I'd throw in my $0.02, only because I came here looking to solve your problem and it seems none of the answers actually work.

You are nearly there in your solution. Your mapping for the HasMany is correct. The problem is, as you've said, the Position doesn't get updated in the database (FWIW when you set it to null, it only "works" because NULL gets inserted into the DB; to me that's not really working ;)).

I find it a bit strange, you need to Map the position on your Static class too, and obviously include a Position property on Static. You don't want the Position property to be writable though, because it's value is determined by where Static is in the list.

Add the following to 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 
    {
    }
}

With that in the Position column in the db will be updated (remember to map your Position in your StaticMap).

I'm guessing the NHib proxy of Static can update the Position field depending on it's position in the list (by some magic greater than I possess) and that then gets saved to the db.

没有你我更好 2024-08-08 20:32:08

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

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

In the Static's table you have a field called "SectionID" or something similar.
Let this field be NULLable: NHibernate first adds the new record, then updates the referenced id.

I have found that in my db and I am surprised too: why the NH doesn't like proper table references at database level?

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文