繁体   English   中英

NHibernate通过代码和SQLite数据库进行映射:保存多对一的父子实体,子类获取null外键

[英]NHibernate with mapping by code and a SQLite database: saving many-to-one parent-child entities, child gets a null foreign key

这个问题的变化已被要求,并回答了很多次,答案有很多重叠的细节。 我已经尝试过这些答案提出的许多不同的事情,但是没有一个在我的案例中有效。

我有一个带有父表和子表的SQLite数据库。 这是一个非常简单的设置。 我正在使用NHibernate 4.0.4进行代码映射而不是流畅,因为它向我建议前者是较新的并且是对后者的改进

实体:

public class BillingItem
{
    public virtual int ID { get; set; }
    public virtual string Name { get; set; }
    // ... other properties
    public virtual ICollection<PaymentItem> PaymentItems { get; set; }

    public BillingItem()
    {
        PaymentItems = new List<PaymentItem>();
    }
}

public class PaymentItem
{
    public virtual int ID { get; set; }
    public virtual BillingItem OwningBillingItem { get; set; }
    // ... other properties
}

BillingItem映射:

public class BillingItemMapping : ClassMapping<BillingItem> 
{
    public BillingItemMapping()
    {
        Table("BillingItems");
        Lazy(true);
        Id(x => x.ID, map => map.Generator(Generators.Identity));

        Set(x => x.PaymentItems, c =>
            {
                c.Key(k =>
                    {
                        k.Column("ID");
                        k.ForeignKey("BillingItemID");
                    });
                c.Inverse(true);
                c.Cascade(Cascade.None);
            },
            r => r.OneToMany(o => { }));

        Property(x => x.Name);
        // ... other properties
    }
}

PaymentItem映射:

public class PaymentItemMapping  : ClassMapping<PaymentItem> 
{
    public PaymentItemMapping()
    {
        Table("PaymentItems");
        Lazy(true);
        Id(x => x.ID, map => map.Generator(Generators.Identity));

        ManyToOne(x => x.OwningBillingItem, m =>
            {
                m.Column("ID");
                m.Update(false);
                m.Insert(false);
                m.Cascade(Cascade.None);
                m.Fetch(FetchKind.Join);
                m.NotFound(NotFoundMode.Exception);
                m.Lazy(LazyRelation.Proxy);
                m.ForeignKey("BillingItemID");
            });

        Property(x => x.DueDate, map => map.NotNullable(true));
        // ... other properties.
    }
}

仓库:

public void Add(BillingItem toAdd)
{
    using (ISession session = Helpers.NHibernateHelper.OpenSession())
    using (ITransaction tran = session.BeginTransaction())
    {
        session.Save(toAdd);

        foreach (var pi in toAdd.PaymentItems)
        {
            session.Save(pi);
        }

        tran.Commit();
    }
}

商业逻辑:

var bi = new BillingItem()
{
    Name = Guid.NewGuid().ToString(),
    // ... others..
};

var pi = new PaymentItem()
{
    OwningBillingItem = bi,
    DueDate = DateTime.Now.AddDays(3)
    // ... others..
};

bi.PaymentItems.Add(pi);
var repo = new Repository();
repo.Add(bi);

正如建议这个答案 (和这个这个和许多其他人),我已经确定设置Inverse(true)在我的Set中(子集) BillingItemMapping 我还在PaymentItem对象中设置了我的双向引用:

OwningBillingItem = bi

BillingItem对象:

bi.PaymentItems.Add(pi);

我觉得我已经按照它应该的方式设置了所有其他内容,并且我根据各种其他来源的建议对许多映射设置进行了修改。 但是,我只是想不通为什么它不起作用。

问题是,我无法获取PaymentItem记录上的外键列来保存BillingItem的ID。 如果我将列设置为不允许空值(这应该是它的方式),我得到一个空约束异常。 如果我将其设置为允许空值(用于测试),它只是设置为null(显然)。

我究竟做错了什么?

呵呵,你的PaymentItemMapping ,正确的映射应该是这样的:

public class PaymentItemMapping : ClassMapping<PaymentItem> {

    public PaymentItemMapping() {
        Table("PaymentItems");
        Lazy(true);
        Id(x => x.ID, map => map.Generator(Generators.Identity));

        ManyToOne(x => x.OwningBillingItem, m => {
            //Do not map to m.Column("ID");
            m.Column("BillingItemID");
            // BillingItemID can be insert and update
            m.Update(true);
            m.Insert(true);
            m.Cascade(Cascade.None);
            m.Fetch(FetchKind.Join);
            m.NotFound(NotFoundMode.Exception);
            m.Lazy(LazyRelation.Proxy);
            m.ForeignKey("BillingItemID");
        });

        Property(x => x.DueDate, map => map.NotNullable(true));
        // ... other properties.
    }
}

映射的一部分似乎是错误的,是一setcolumn

// BillingItemMapping()
Set(x => x.PaymentItems, c =>
{
    // this should refer to column where parent id will be found
    c.Key(k =>
    {
        k.Column("ID");
        k.ForeignKey("BillingItemID");
    });

所以它应该是

    c.Key(k =>
    {
        k.Column("BillingItemID");
    });

外键只适用于sql生成器..现在可以跳过它

此外,对于每个集合,我使用级联

Set(x => x.PaymentItems, c =>
{
    c.Key(k =>
    {
        k.Column("BillingItemID");
    });
    c.Inverse(true);
    //c.Cascade(Cascade.None);
    c.Cascade(Cascade.All);
},

有了这个,我们可以简化调用以坚持所有

using (ISession session = Helpers.NHibernateHelper.OpenSession())
using (ITransaction tran = session.BeginTransaction())
{
    session.Save(toAdd);

    //foreach (var pi in toAdd.PaymentItems)
    //{
    //    session.Save(pi);
    //}

    tran.Commit();
}

(我们仍然需要保留两个引用parent-child / child-parent)

最后 - 当我们有级联 - 我们必须允许NHibernate完成它的工作 - INSERT和UPDATE这种关系

ManyToOne(x => x.OwningBillingItem, m =>
{
    m.Column("ID");
    // this is stopper - DO NOT use it
    //m.Update(false);
    //m.Insert(false);

因此,如果我们想要更新和插入,请不要设置Update(false)Insert(false) 这应该解决所有..

这也可以得到一些见解:

使用NHibernate映射一对多的最小和正确方法

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM