【问题标题】:Removing an item from a collection(NHibernate)从集合中删除一个项目(NHibernate)
【发布时间】:2011-01-17 14:46:43
【问题描述】:

我在两个实体(Parent 和 Child)之间有父子关系。

我的 Parent 映射如下:

<class name="Parent" table="Parents">
    ...
    <bag name="Children" cascade="all">
        <key column="ParentID"></key>
        <one-to-many class="Child"></one-to-many>
    </bag>
</class>

我想执行以下操作:

someParent.Children.Remove(someChild);

Child 类引用了另一个父类 Type。关系看起来像

注意:对于上面的非链接网址,我深表歉意,我似乎无法使用标记跳过网址字符串中的星号

由于这种关系,当调用上述代码时,会生成一个 UPDATE 查询,该查询从子表中删除 ParentID(设置为 null),而不是 DELETE 查询。

当从 Parent.Children 集合中删除时,是否可以强制 NHibernate 完全删除子记录?

更新

@Spencer 的解决方案

非常有吸引力的解决方案,因为这可以在未来的课程中实现。但是,由于在存储库模式中处理会话的方式(在我的特定情况下),这几乎是不可能的,因为我们必须根据应用程序传递会话类型(CallSessionContext/WebSessionContext)。

@Jamie 的解决方案

实施简单且快速,但我遇到了另一个障碍。我的子实体如下所示:

使用新方法时,NHibernate 会生成一条更新语句,将 TypeID 和 ParentID 设置为 null,而不是直接删除。如果我在实施过程中遗漏了某些内容,请告诉我,因为这种方法可以轻松推进。

@The One-Shot-Delete solution described here,概述了取消引用集合以强制单个删除的想法。结果与上面相同,但是发出了更新语句。

//Instantiate new collection and add persisted items
List<Child> children = new List<Child>();
children.AddRange(parent.Children);

//Find and remove requested items from new collection
var childrenToRemove = children
    .Where(c => c.Type.TypeID == 1)
    .ToList();

foreach (var c in childrenToRemove) { children.Remove(m); }
parent.Children = null;

//Set persisted collection to new list
parent.Children = Children;

解决方案

进行了一些挖掘,但 Jamie 的解决方案通过一些额外的修改得以实现。对于未来的读者,基于我上面的类模型:

类型映射 - Inverse = true,Cascade = all

父映射 - Inverse = true, Cascade = all-delete-orphan

删除 Jamie 解决方案中描述的方法。这确实会为每个孤立项生成一个删除语句,因此将来有可能进行调整,但最终结果是成功的。

【问题讨论】:

    标签: c# nhibernate


    【解决方案1】:

    不是暴露IList&lt;Child&gt;,而是通过一个方法控制对集合的访问:

    RemoveChild(Child child)
    {
        Children.Remove(child);
        child.Parent = null;
        child.Type.RemoveChild(child);
    }
    

    Type.RemoveChild 看起来很相似,但您必须小心不要将其放入调用彼此的 RemoveChild 方法的无限循环中。

    【讨论】:

      【解决方案2】:

      我不认为这是完全可能的,因为 Hibernate 无法知道记录是否已成为孤立记录。它可以检查是否有任何其他类与子类相关,但它会假设它知道整个 DB 结构,但情况可能并非如此。

      不过,您并非完全不走运。通过将 IList 接口与您创建的自定义 ICascadeDeleteChild 接口结合使用,您可以提出一个相当无缝的解决方案。以下是基本步骤。

      1. 创建一个继承 IList 和 IList 的类,并将其命名为 CascadeDeleteList 或类似的名称。
      2. 在该类中创建一个私有 .Net 列表,并简单地将各种方法调用代理到该列表。
      3. 创建一个名为 ICascadeDeleteChild 的接口并为其提供方法 Delete()
      4. 在 CascadeDeleteList 的 Delete 方法下,检查要删除的对象的类型。如果它是 ICascadeDeleteChild 类型,则调用它的 Delete 方法。
      5. 更改您的 Child 类以实现 ICascadeDeleteChild 接口。

      我知道这看起来很痛苦,但是一旦完成,这些接口应该很容易移植。

      【讨论】:

      • 我相信这可能让我走上了正确的道路,障碍是:在存储库模式中设置它(即,在这种情况下,子 Delete 方法将调用它自己的存储库方法)和还能够将其用于不同的会话上下文(即 CallSessionContext 与 WebSessionContext)
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-12
      • 2013-01-17
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多