NHibernate级联。仍然没有更新父实体

时间:2019-08-12 15:54:20

标签: c# nhibernate

我有一些实体(为简单起见,它看起来像这样):

class DbEntity
{
    public Guid Id {get;set;}
}

class BaseEntity: DbEntity
{
   public string Name {get;set;}
   public ParentEntity Parent {get;set;}       
}

class ParentEntity: BaseEntity
{
    List<BaseEntity> Children {get;set;}
}

class ChildEntity: BaseEntity
{

}

现在,我的流利映射看起来像这样:

class DbEntityMap<T>: ClassMap<T> where T: DbEntity
{
    public DbEntityMap()
    {
        Id(x => x.Id).GeneratedBy.GuidComb();
    }
}

class BaseEntityMap: DbEntityMap<BaseEntity>
{
    UseUnionSubclassForInheritanceMapping();
    Map(x => x.Name).Not.Nullable();

    References(x => x.Parent).Cascade.None();
}

class ParentEntityMap: SubclassMap<ParentEntity>
{
   Abstract();
   HasMany(x => x.Children)
       .ForeignKeyCascadeOnDelete()
       .Inverse()
       .Not.KeyUpdate()
       .Cascade.None()
       .LazyLoad();
}

class ChildEntityMap: SubclassMap<ChildEntity>
{
   Abstract();
   //some other stuff
}

现在,我有将请求发送到WebAPI的客户端桌面应用程序。 第一个请求是创建所有实体,例如:

ParentEntity parent = new ParentEntity();
parent.Name = "New name";

ChildEntity child1 = new ChildEntity();
ChildEntity child2 = new ChildEntity();

parent.Children.Add(child1);
parent.Children.Add(child2);

//of course children know their parent:
//child.Parent = parent;

现在,我正在从中构建一些dto(因为我的模型要复杂得多,并且创建dto是个好主意)来构建json,并将此json发送到WebAPI。数据库中的实体已正确创建。很好。

但是现在我必须修改一些子实体,例如:

child1.SomeValue = newValue;

现在,在创建DTO时,我不会向其添加父实体,只需添加父ID,它看起来就更少了:

class ChildDto
{
    public Guid DbId {get;set;}
    public Guid ParentDbId {get;set;}
    public int SomeValue {get;set;}
}

现在,当我的WebAPI收到此类dto时,它会重新创建一个像这样的模型:

ParentEntity fakeParent = new ParentEntity();
fakeParent.Id = childDto.ParentDbId;

ChildEntity child = new ChildEntity();
//assign other child values and then parent:
child.Parent = fakeParent;

现在,当我执行Update(只是session.Update(obj,id))时,我的子实体会正确更新,但父实体也会更新。由于我没有在父实体中设置Name属性,因此数据库中的Name字段变为空。

我认为将Cascade设置为None将阻止NHibernate更新父实体。但是没有级联设置有效。如果我将父绑定设置为:

References(x => x.Parent).ReadOnly()

然后我的ChildEntity会更新而没有父级ID。

我至少知道几种解决方案,例如: 1.在DTO中也转换整个父实体-但这可以进行其他级联更新,如果没有,则将进行两次更新而不是一次。 2.在更新之前选择父实体-但这会创建不必要的SELECT。

我想要实现的是: -仅更新childEntity-无需更新父级或任何其他实体。

我该如何完成呢?

[关于实际应用的一些话]

实际上,我的ParentEntity可以容纳BaseEntities。 ParentEntity和ChildEntity也具有一些共同的属性。

此外,ChildEntity可以容纳其他DbEntities。 子实体的子代还拥有其他DbEntities,如下所示:

ParentEntity-> ChildEntity-> EntityA-> EntityB-> EntityC (就像一棵树)

(每个派生自DbEntity)。 而且我什至设法使用session.Merge解决了问题,但是当我尝试更新EntityA时,问题还是一样的-ChildEntity与ParentEntity失去连接:|

2 个答案:

答案 0 :(得分:1)

  

我认为将Cascade设置为None将阻止NHibernate更新父实体。但是级联设置无效

您期望Cascade.None将阻止更新关联。尽管这是事实,但您的代码中并非如此。由于您的 strange 继承层次结构,父实体正在更新。我不了解需要多级继承。如果您删除了BaseEntity,那么您将无需致电Load就可以实现想要实现的目标。

here说明了您正在使用的方案:

  

10.4.2。更新分离的对象

     

许多应用程序需要在一个事务中检索对象,将其发送到UI层进行操作,然后将更改保存在新事务中。 ........
  这种方法需要与上一节中描述的编程模型稍有不同的编程模型。 NHibernate通过提供方法ISession.Update()支持此模型。

您还应该查看this

  

10.10。生命周期和对象图

     

级联操作的精确语义如下:

     
      
  • 如果保存了父项,则所有子项都将传递到SaveOrUpdate()
  •   
  • 如果将父级传递给Update()或SaveOrUpdate(),则所有子级都传递给SaveOrUpdate()
  •   
  • 如果持久子级引用了临时子级,则将其传递给SaveOrUpdate()
  •   
  • 如果删除了父级,则所有子级都会传递给Delete()
  •   
  • 如果持久子级取消了临时子级的引用,则不会发生任何特殊情况(应用程序应在必要时显式删除该子级),除非cascade =“ all-delete-orphan”或cascade =“ delete-orphan”,在这种情况下“孤儿”被删除。
  •   

注意上面突出显示的点。您的ParentEntityChildEntity均来自BaseEntity。此外,BaseEntity拥有关联属性ParentEntity Parent。当您Update子实体时,您的父实体将传递给SaveOrUpdate。由于您尚未在此处指定Name,因此它将使用空名称更新数据库。

  

如果我将父绑定设置为:

References(x => x.Parent).ReadOnly()
     

然后我的ChildEntity会更新而没有父级ID。

这很明显;您已将其明确设置为只读。

答案 1 :(得分:0)

您应该session.Load不想更新的现有对象(不要将其与session.Get混淆-它不会为惰性实体发出SELECT语句)。

因此,您需要将DTO->实体转换逻辑更改为以下内容:

ParentEntity fakeParent = session.Load<ParentEntity>(childDto.ParentDbId);

ChildEntity child = new ChildEntity();
//assign other child values and then parent:
child.Parent = fakeParent;