是否可以Cascade.All()除了删除?

时间:2014-01-27 21:14:22

标签: c# nhibernate nhibernate-mapping

我分别获取一个对象列表(而不是NHibernate),并将父对象IEnumerable设置为等于此返回的对象。最初,我们只需要阅读对象。然后我们需要只更新父项的特定字段。最近,我们需要更新孩子的字段。到目前为止,SaveOrUpdate()一切都很顺利。现在,即使将子集合附加到分离的父对象(不使用NHibernate),我也需要更新子节点。以下代码导致父更新,但不是子项。如果我做了所有,那么如果父母没有集合,孩子将被删除。我不想这样做,因为我担心遗留的使用不会解释这种行为。

期望的行为:
 1.级联对集合的任何更改(无论是否在父级中由NHibernate检索)。  2.即使父母没有儿童集合,也不要删除对象。

这可能吗?

这是我们的NHibernate保存方法:

[Transaction]
public int? Save(DocumentFieldDTO entity, bool autoFlush)
{
    var persisted = CurrentSession.Merge(entity);

    entity.DocumentFieldID = persisted.DocumentFieldID;
    if (autoFlush) { CurrentSession.Flush(); }
    return entity.DocumentFieldID;
}

DocumentFieldDTOMap看起来像这样:

public class DocumentFieldDTOMap : EntityMapBase
{

    public DocumentFieldDTOMap()
    {    
        Table("DocumentField");

        Id(m => m.DocumentFieldID).GeneratedBy.Increment().UnsavedValue(null);

        Map(x => x.Name);

        Map(x => x.DocumentSectionID).Not.Update();
        // .... Lots of other fields ....//

        HasMany(x => x.DocumentFieldOrgs)
        .Cascade.SaveUpdate()
        .LazyLoad()
        .KeyColumn("DocumentFieldID");
        }
    }

}

如果我将Cascade.SaveUpdate()更改为Cascade.All(),则更新有效,但也会删除。我想消除删除功能。

更新(2014年1月27日):

我刚刚验证了映射为SaveUpdate()时删除是级联的,所以这不是一个大问题,因为我没有更改现有功能。我仍然希望能够更新所有级联EXCEPT删除。如果可能的话,解决方案将非常适合将来参考。

更新(2014年2月2日)

以下是在级联为“SaveUpdate()”时验证子项被删除的测试。 GetDocumentFieldDTOWithADO(DocumentFieldID)使用与NHibernate相同的事务,在第一次调用时(保存前)有318个DocumentFieldOrgs,在保存后调用时有0。也许这个测试存在问题?是否删除了孩子因为我打电话给Merge?

    [Test]
    public void Save_ShouldDeleteDocumentFieldOrgs_WhenSavingDocumentFieldWithoutDocFieldOrgsList()
    {
        //arrange
        var expectedDocField = GetDocumentFieldDTOWithADO(DocumentFieldID);
        expectedDocField.DocumentFieldOrgs = null;

        //act
        Repository.Save(expectedDocField, false);
        SessionFactory.GetCurrentSession().FlushAndEvict(expectedDocField);

        //assert
        var actualDocField = GetDocumentFieldDTOWithADO(DocumentFieldID);

        actualDocField.DocumentFieldOrgs.Should()
            .BeEmpty("DocumentFieldOrgs should be deleted if the parent does not have a child collection");        
    }

更新(2014年2月2日) - Radim在下面的答案中是正确的。 NHibernate没有删除孩子们。它使他们与父母脱离关系。

1 个答案:

答案 0 :(得分:1)

更新,反映查询更改

您在查询更新中展示的测试已经证明:

  

如果parent.Children设置为null并且已持续,则下次访问时将没有子项。

让我解释一下发生了什么,让我使用一些虚拟语言(注意我使用 Parent Children 来制作很简单)

1)父子的映射是cascade="save-update"
这是NHibernate的一个信息,在创建或修改期间,子集合应该通过Save()或Update()调用

2)让我们加载父

var session = ... // get a ISession for our test
var parent = session.Get<Parent>(1); // e.g. DocumentFieldDTO 

// NOT Empty -- is true:  IsNotEmpty()
Assert.IsTrue(parent.Children.IsNotEmpty()); // e.g. DocumentFieldOrgs

3)现在,删除引用并检查NHibernate将执行的操作:

parent.Children = null;

session.Flush();
session.Clear();

这是执行的SQL语句:

exec sp_executesql N'UPDATE [schema].[Child_Table] 
      SET ParentId = null WHERE ParentId = @p0',N'@p0 int',@p0=1

正如我们所看到的,由于映射save-update,NHibernate通过删除引用确实处理了这种情况。实际上通过UPDATing子表

4)再次加载Parent

parent = session.Get<Parent>(1);

// EMPTY -- is true: IsEmpty()
Assert.IsTrue(parent.Children.IsEmpty());

要点: 正如我们在上面看到的那样,NHibernate正在做着映射和预期的事情。没有删除。只是更新,删除参考

本答案的上一部分

答案是:更改public int? Save(...)实施。 NHibernate级联按预期工作,请在此处阅读更多Ayende, NHibernate Cascades: the different between all, all-delete-orphans and save-update

首先看看上面的陈述:

  

期望的行为:
  1)级联对集合的任何更改(是否在父级中由NHibernate检索)。
  2)即使父级没有子集合,也不要删除对象。

大胆的部分是Cascade concept无效的原因。这是因为:

  

Cascade只有在执行操作时才有意义    现有父级是级联/重复/传递的    现有/已知子(儿童)

NHiberante级联实现确实以这种方式工作:9.9. Lifecyles and object graphs (摘录)

  

映射...与cascade="all"将关联标记为父/子样式关系,其中父项的保存/更新/删除导致保存/更新/删除子项(ren)。 ... 由于其父级未被引用的子项不会自动删除,除非使用cascade =“all-delete-orphan”映射<one-to-many>关联... ... < / p>

不仅没有删除。如果未引用,则不会接收任何类型的级联操作的触发器。

建议:

调整Save()方法,进行两项操作:

  1. 更新父
  2. 找到“children”或更好 - 以某种方式相关的项目。加载它们进行调整,然后调用session.Flush()。对 ISession 引用的对象的任何更改都将保留。