父/子在同一个表中流利的Nhibernate异常?

时间:2014-08-22 15:22:51

标签: c# nhibernate fluent-nhibernate

保存时出现此错误:
"对象引用未保存的瞬态实例 - 在刷新之前保存瞬态实例,或者将属性的级联操作设置为使其自动保存的内容。"
实体:

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();

上面的错误异常发生!!
问题在哪里?

1 个答案:

答案 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:例外说:

  1. 对象引用... (这很可能是DivisionType)
  2. 未保存的瞬态实例.. (是的,属性DivisionType是在构造函数中创建的==它是瞬态实例)
  3. 在刷新之前保存瞬态实例,或者将属性的级联操作设置为使其自动保存的内容。
  4. 类型:HR.Core.Data.Entities.Division ... (这是具有参考的类型)
  5. 因此,由于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