HNibernate 1 对多关系 仅使用外键 Id 的流畅 Niberate

发布于 2024-10-14 14:11:31 字数 2845 浏览 2 评论 0原文

我想在 NHibernate 中建立一对多关系,其中 Child 表只能访问它的parentsId。或者数据库中的外键。

我已尝试以下设置:

public class ParentTable
{
    public ParentTable()
    {
        _childRecords = new List<ChildTable>();
    }

    public virtual int ParentId { get; set; }

    private IList<ChildTable> _childRecords;

    public virtual IEnumerable<ChildTable> ChildRecords
    {
        get { return _childRecords; }
    }

    public void AddChildTable(string value)
    {
        _childRecords.Add(new ChildTable{ StringField = value });
    }
}

public class ChildTable
{

    public virtual int ChildTableId { get; set; }
    public virtual string StringField { get; set; }
    public virtual int ParentId { get; set; }
}

映射:

public class ParentTableMap : ClassMap<ParentTable>
{
    public ParentTableMap()
    {
        Not.LazyLoad();

        Id(x => x.ParentId);

        HasMany(x => x.ChildRecords)
            .Not.LazyLoad()
            .KeyColumn("ParentId").Cascade.All()
            .Access.ReadOnlyPropertyThroughCamelCaseField(Prefix.Underscore);
    }
}

public class ChildTableMap : ClassMap<ChildTable>
{
    public ChildTableMap()
    {
        Not.LazyLoad();

        Id(x => x.ChildTableId);

        Map(x => x.StringField);
        Map(x => x.ParentId).Not.Nullable();
    }

}

以下测试失败,因为它试图将 0 插入 ParentId 列?

[TestFixture]
public class Tests
{
    [Test]
    public void SaveOrUpdate_ParentWithChildren_WillCreateParentWithChildRecordsHavingMatchingParentId()
    {

        int id;
        using (var sessionForInsert = SessionProvider.SessionFactory.OpenSession())
        {
            using (var trx = sessionForInsert.BeginTransaction())
            {
                //Assign
                var parent = new ParentTable();
                parent.AddChildTable("Testing");
                parent.AddChildTable("Testing2");
                sessionForInsert.SaveOrUpdate(parent); // Fails here with DB constraint error 
                id = parent.ParentId;
            }
        }

        using (var sessionForSelect = SessionProvider.SessionFactory.OpenSession())
        {
            //Action
            var result = sessionForSelect.Get<ParentTable>(id);
            Assert.AreEqual(id, result.ParentId);
            Assert.AreEqual(id, result.ChildRecords.First().ParentId);
            Assert.AreEqual(id, result.ChildRecords.Last().ParentId);
        }

    }
}

这就是它想要做的:

exec sp_executesql N'INSERT INTO ChildTable (StringField, ParentId) VALUES (@p0, @p1); select SCOPE_IDENTITY()',N'@p0 nvarchar,@p1 int',@p0='Testing;,@p1=0

我意识到我可以在子类中设置对父类的引用。但是,由于循环引用以及序列化和反序列化这些类时会导致的问题,我希望尽可能避免这种情况。

有人成功建立了像上面这样的一对多关系吗?

谢谢

戴夫

I'm wanting to have a 1 to many relationship in NHibernate where the Child table only has access to it's parentsId. Or the foreign key in the DB.

I've tried the following setup:

public class ParentTable
{
    public ParentTable()
    {
        _childRecords = new List<ChildTable>();
    }

    public virtual int ParentId { get; set; }

    private IList<ChildTable> _childRecords;

    public virtual IEnumerable<ChildTable> ChildRecords
    {
        get { return _childRecords; }
    }

    public void AddChildTable(string value)
    {
        _childRecords.Add(new ChildTable{ StringField = value });
    }
}

public class ChildTable
{

    public virtual int ChildTableId { get; set; }
    public virtual string StringField { get; set; }
    public virtual int ParentId { get; set; }
}

Mappings:

public class ParentTableMap : ClassMap<ParentTable>
{
    public ParentTableMap()
    {
        Not.LazyLoad();

        Id(x => x.ParentId);

        HasMany(x => x.ChildRecords)
            .Not.LazyLoad()
            .KeyColumn("ParentId").Cascade.All()
            .Access.ReadOnlyPropertyThroughCamelCaseField(Prefix.Underscore);
    }
}

public class ChildTableMap : ClassMap<ChildTable>
{
    public ChildTableMap()
    {
        Not.LazyLoad();

        Id(x => x.ChildTableId);

        Map(x => x.StringField);
        Map(x => x.ParentId).Not.Nullable();
    }

}

The following test fails as it's trying to insert 0 into the ParentId column?

[TestFixture]
public class Tests
{
    [Test]
    public void SaveOrUpdate_ParentWithChildren_WillCreateParentWithChildRecordsHavingMatchingParentId()
    {

        int id;
        using (var sessionForInsert = SessionProvider.SessionFactory.OpenSession())
        {
            using (var trx = sessionForInsert.BeginTransaction())
            {
                //Assign
                var parent = new ParentTable();
                parent.AddChildTable("Testing");
                parent.AddChildTable("Testing2");
                sessionForInsert.SaveOrUpdate(parent); // Fails here with DB constraint error 
                id = parent.ParentId;
            }
        }

        using (var sessionForSelect = SessionProvider.SessionFactory.OpenSession())
        {
            //Action
            var result = sessionForSelect.Get<ParentTable>(id);
            Assert.AreEqual(id, result.ParentId);
            Assert.AreEqual(id, result.ChildRecords.First().ParentId);
            Assert.AreEqual(id, result.ChildRecords.Last().ParentId);
        }

    }
}

This is what it's trying to do:

exec sp_executesql N'INSERT INTO ChildTable (StringField, ParentId) VALUES (@p0, @p1); select SCOPE_IDENTITY()',N'@p0 nvarchar,@p1 int',@p0='Testing;,@p1=0

I realise I could set-up a reference to the Parent Class in the Child Class. However I'd like to avoid this if at all possible, due to circular references and the problems that will cause when serializing and de-serializing these classes.

Has anyone successfully set-up and 1 to many relationship like the above?

Thanks

Dave

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

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

发布评论

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

评论(2

心安伴我暖 2024-10-21 14:11:31

我认为您要么需要:

  • 使 ChildTable 上的 ParentId 可以为空,或者
  • 将您的 id 生成器更改为 NHibernate 可以生成的内容。

第二个选择很好。切换到 Guid.Comb 查找您的 ID。对象关系映射器可以执行的操作受到限制。具体来说,建议让 NHibernate 生成 id 而不是数据库。我认为这篇(长)博客文章详细解释了它: http: //fabiomaulo.blogspot.com/2009/02/nh210-generators-behavior-explained.html

祝你好运!

I think you either need to:

  • Make the ParentId on ChildTable nullable, or
  • Change your id generators to something NHibernate can generate.

The second option is nice. Switch to Guid.Comb for your id's. There's a restriction on what object relational mappers can do. Specifically, it is recommended to let NHibernate generate the id's instead of the database. I think this (long) blog post explains it in detail: http://fabiomaulo.blogspot.com/2009/02/nh210-generators-behavior-explained.html.

Good luck!

断念 2024-10-21 14:11:31

问题是您试图在一个操作中插入父级及其子级。为此,NHibernate 希望插入具有 null ParentId 的子记录,然后在插入父记录后更新 ParentId。此外键约束导致此操作失败。

最好的解决方案是映射从孩子到父母的关系。您不必公开公开父级,您可以将其 ParentId 公开为 int 吗?如果需要的话。

如果这是不可接受的,您应该能够通过更改操作顺序来完成此操作。首先,我需要 ChildTable 的构造函数中的 ParentId。然后更改测试中的操作顺序即可通过。

public class ChildTable
{
    public ChildTable(int parentId) { ParentId = parentId; }
    public virtual int ChildTableId { get; set; }
    public virtual string StringField { get; set; }
    public virtual int ParentId { get; private set; }
}

using (var trx = sessionForInsert.BeginTransaction())
{
    //Assign
    var parent = new ParentTable();
    sessionForInsert.Save(parent);
    sessionForInsert.Flush(); // may not be needed
    parent.AddChildTable("Testing");
    parent.AddChildTable("Testing2");
    trx.Commit();
    id = parent.ParentId;
}

编辑:

public class ChildTable
{
    private ParentTable _parent;

    public ChildTable(Parent parent) { _parent = parent; }

    public virtual int ChildTableId { get; set; }
    public virtual string StringField { get; set; }
    public virtual int? ParentId 
    {
        get { return _parent == null : null ? _parent.ParentId; }
    }
}

public class ChildTableMap : ClassMap<ChildTable>
{
    public ChildTableMap()
    {
        Not.LazyLoad();

        Id(x => x.ChildTableId);

        Map(x => x.StringField);
        // From memory, I probably have this syntax wrong...
        References(Reveal.Property<ParentTable>("Parent"), "ParentTableId")
            .Access.CamelCaseField(Prefix.Underscore);
    }
}

The problem is that you are attempting to insert a parent and its children in one operation. To do this, NHibernate wants to insert the child records with a null ParentId then update ParentId after the parent record is inserted. This foreign key constraint causes this to fail.

The best solution is to map the relationship from child to parent. You don't have to publicly expose the parent, you could just expose its ParentId as int? if desired.

If that's unacceptable, you should be able to accomplish this by changing the order of operations. First, I would require the ParentId in ChildTable's constructor. Then change the operation order in the test to get it to pass.

public class ChildTable
{
    public ChildTable(int parentId) { ParentId = parentId; }
    public virtual int ChildTableId { get; set; }
    public virtual string StringField { get; set; }
    public virtual int ParentId { get; private set; }
}

using (var trx = sessionForInsert.BeginTransaction())
{
    //Assign
    var parent = new ParentTable();
    sessionForInsert.Save(parent);
    sessionForInsert.Flush(); // may not be needed
    parent.AddChildTable("Testing");
    parent.AddChildTable("Testing2");
    trx.Commit();
    id = parent.ParentId;
}

EDIT:

public class ChildTable
{
    private ParentTable _parent;

    public ChildTable(Parent parent) { _parent = parent; }

    public virtual int ChildTableId { get; set; }
    public virtual string StringField { get; set; }
    public virtual int? ParentId 
    {
        get { return _parent == null : null ? _parent.ParentId; }
    }
}

public class ChildTableMap : ClassMap<ChildTable>
{
    public ChildTableMap()
    {
        Not.LazyLoad();

        Id(x => x.ChildTableId);

        Map(x => x.StringField);
        // From memory, I probably have this syntax wrong...
        References(Reveal.Property<ParentTable>("Parent"), "ParentTableId")
            .Access.CamelCaseField(Prefix.Underscore);
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文