NHibernate 通过代码映射和 SQLite 数据库:保存多对一的父子实体,子实体获得空外键
NHibernate with mapping by code and a SQLite database: saving many-to-one parent-child entities, child gets a null foreign key
这个问题的变体已被问及 多次 次回答,答案有很多重叠的细节。我已经尝试了这些答案所建议的许多不同的事情,但是 none 对我来说是有效的。
我有一个带有父 table 和子 table 的 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
}
计费项目映射:
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
}
}
付款项映射:
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);
正如 this answer (and this and 和许多其他人所建议的那样,我已确保在 BillingItemMapping
中的 Set
(子集合)中设置 Inverse(true)
。我还在 PaymentItem
对象中设置了我的双向引用:
OwningBillingItem = bi
和BillingItem
对象:
bi.PaymentItems.Add(pi);
我觉得我已经按照应有的方式设置了所有其他内容,并且我已经根据其他各种来源的建议修改了许多映射设置。但是,我只是想不通为什么它不起作用。
问题是,我无法获取 PaymentItem
记录上的外键列来保存来自 BillingItem
的 ID。如果我将列设置为不允许空值(这是应该的方式),我会得到一个空约束异常。如果我将它设置为允许空值(用于测试),它只会被设置为空值(很明显)。
我做错了什么?
映射的一部分似乎是错误的,是 set
的 column
// 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();
}
(我们仍然需要保留两个引用父子/子父)
最后 - 当我们有级联时 - 我们必须允许 NHibernate 完成它的工作 - 插入和更新该关系
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)
。那应该可以解决所有问题..
这也可以得到一些启示:
呵呵,你的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.
}
}
这个问题的变体已被问及 多次 次回答,答案有很多重叠的细节。我已经尝试了这些答案所建议的许多不同的事情,但是 none 对我来说是有效的。
我有一个带有父 table 和子 table 的 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
}
计费项目映射:
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
}
}
付款项映射:
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);
正如 this answer (and this and BillingItemMapping
中的 Set
(子集合)中设置 Inverse(true)
。我还在 PaymentItem
对象中设置了我的双向引用:
OwningBillingItem = bi
和BillingItem
对象:
bi.PaymentItems.Add(pi);
我觉得我已经按照应有的方式设置了所有其他内容,并且我已经根据其他各种来源的建议修改了许多映射设置。但是,我只是想不通为什么它不起作用。
问题是,我无法获取 PaymentItem
记录上的外键列来保存来自 BillingItem
的 ID。如果我将列设置为不允许空值(这是应该的方式),我会得到一个空约束异常。如果我将它设置为允许空值(用于测试),它只会被设置为空值(很明显)。
我做错了什么?
映射的一部分似乎是错误的,是 set
column
// 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();
}
(我们仍然需要保留两个引用父子/子父)
最后 - 当我们有级联时 - 我们必须允许 NHibernate 完成它的工作 - 插入和更新该关系
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)
。那应该可以解决所有问题..
这也可以得到一些启示:
呵呵,你的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.
}
}