在同一表中的父/子节点中出现Fluent Nhibernate异常

本文关键字:Fluent 异常 Nhibernate 子节点 | 更新日期: 2023-09-27 18:19:11

我有这个错误发生时,保存:
"对象引用未保存的瞬态实例-在刷新之前保存该瞬态实例,或将该属性的级联动作设置为可使其自动保存的内容。"


实体:
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();
    }
}

如您所见,我有division及其父节点和子节点。当我呼叫:

Session.SaveOrUpdate(entity);
NhUnitOfWork.Current.Commit();

出现上面的错误异常!!
问题在哪里?

在同一表中的父/子节点中出现Fluent Nhibernate异常

在构造函数中创建了新的实例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:不要在构造函数

中调用可重写的方法

EXTEND:异常说:

  1. 对象引用…(这很可能是DivisionType)
  2. 一个未保存的瞬态实例。(是的,DivisionType属性是在构造函数中创建的==它是瞬态实例)
  3. 在刷新之前保存瞬态实例或将属性的级联动作设置为使其自动保存的内容。
  4. 类型:HR.Core.Data.Entities.Division…(这是有引用的类型)

因此,因为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 - Get, Load和通过id

查询的区别