保存时出现此错误:
"对象引用未保存的瞬态实例 - 在刷新之前保存瞬态实例,或者将属性的级联操作设置为使其自动保存的内容。"
实体:
public class Division : EntityBase<int>
{
public Division()
: base()
{
DivisionType = new DivisionType();
Employees = new List<Employee>();
DivisionChildren = new List<Division>();
}
public virtual string DivisionName { get; set; }
public virtual DivisionType DivisionType { get; set; }
public virtual Division ParentDivision { get; set; }
public virtual IList<Division> DivisionChildren { get; set; }
public virtual IList<Employee> Employees { get; set; }
}
映射:
public class DivisionMap : ClassMap<Division>
{
public DivisionMap()
{
Table("param.Division");
Id(x => x.Id).Column("DivisionId");
Map(x => x.DivisionName);
References(x => x.DivisionType).Column("DivisionTypeId");
References(x => x.ParentDivision).Column("ParentDivisionId");
HasMany(x => x.DivisionChildren).KeyColumn("ParentDivisionId").Inverse().Cascade.AllDeleteOrphan();
HasMany(x => x.Employees).KeyColumn("DivisionId").Inverse().Cascade.AllDeleteOrphan();
}
}
正如你所看到的,我有Divison与其父母和孩子。 我打电话的时候:
Session.SaveOrUpdate(entity);
NhUnitOfWork.Current.Commit();
上面的错误异常发生!!
问题在哪里?
答案 0 :(得分:0)
在构造函数中 - 创建了新的实例DivisionType
DivisionType = new DivisionType();
无论如何,映射都不会处理它的级联:
References(x => x.DivisionType).Column("DivisionTypeId");
这是异常的原因。如果你想要一次又一次地创建Type,你也可以介绍Cascade。但最有可能你应该从现有类型的列表中绑定它......
所以,不首选的解决方案(仅在我的想法中)不会是这样的:
References(x => x.DivisionType)
.Column("DivisionTypeId");
.Cascade.All();
但它会奏效。
我建议的方法是获取对现有DivisionType的引用并将其分配
entity.DivisionType = Session.Load<DivisionType>(typeId); // recieved from client
Session.SaveOrUpdate(entity);
NhUnitOfWork.Current.Commit();
注意:我强烈建议更改引用和集合的初始化 - 这些是虚拟属性,这意味着它们不应该在构造函数中创建...将auto属性转换为具有私有支持字段的属性 - 待发起
例如,请参阅 - CA2214: Do not call overridable methods in constructors
EXTEND:例外说:
因此,由于DivisionType 已映射,因此NHibernate会尝试将其ID保留在 "DivisionTypeId"
列中。
但是因为DivisionType没有ID - 它是一个瞬态实例 - 抛出异常。
父/子映射没有问题
扩展更多:
现在我们知道,我们也在某些上层分配了entity.ParentDivision
。最合适和最合适的方法是:
entity.ParentDivision = session.Load<Division>(parentDivisionId);
如果会话不是服务(MVC控制器)层的一部分 - 我们可以创建一些DAO方法,例如:
public virtual Division GetById(id)
{
var session = ... // ISessionFactory gets session
return session.Get<Division>(id);
}
使用NHibernate时这非常重要。如果我们希望NHibernate完成所有级联,那么引用应该通过NHibernate(Get或Load)来实现......
请阅读这个深刻的解释:
NHibernate – The difference between Get, Load and querying by id