【问题标题】:NHibernate: Calling .RemoveAll on a collectionNHibernate:在集合上调用 .RemoveAll
【发布时间】:2012-01-27 11:23:02
【问题描述】:

在通过 NHibernate 检索集合时,我遇到了无法使用 .RemoveAll 的问题。

我有一个名为 Order 的实体,我通过 NHibernate 进行持久化。

Order 有很多 OrderItems。这是我对这种关系的映射:

mapping.HasMany(o => o.Items)
                   .Cascade.AllDeleteOrphan()
                   .AsList()
                   .Inverse();

在我的域中看起来像这样:

public virtual IList<OrderItem> Items { get; set; }

据我了解,我必须使用IList,因为 NHibernate 有它自己的 List 实现。

现在我想在我的Order 类中使用此方法从我的Order 中删除一个项目:

public virtual void RemoveItem(string variantSku)
{
    items.RemoveAll(x => x.Variant.VariantSku == variantSku);
}

不行,因为IList没有这个方法。

我试过了:

items.ToList().RemoveAll(x => x.Variant.VariantSku == variantSku);

但这似乎不起作用。我意识到items.ToList() 实际上创建了原始列表的副本,所以我想我可以试试:

var itemsList = items.ToList();
itemsList.RemoveAll(...)

但是它仍然通过 NHibernate 持续存在吗?

我的问题:我真的可以在这种情况下使用.RemoveAll,还是我应该考虑用不同的方法来删除一个项目?

【问题讨论】:

    标签: c# nhibernate fluent-nhibernate


    【解决方案1】:

    您将集合映射为关系的反面,因此除了从集合中删除项目之外,您还必须取消对 OrderItem 端的 Order 的引用。因此,RemoveAll 方法或扩展方法无法完成这项工作。

    我会这样处理:

    public virtual void RemoveItem(string variantSku)
    {
        var itemsToRemove = items.Where(x => x.Variant.VariantSku == variantSku).ToArray();
        foreach(var item in itemsToRemove)
        {
           item.Order = null;
           items.Remove(item);
        }
    }
    

    我还建议使用集合而不是包映射。

    【讨论】:

    • 如果映射中有 .Cascade.AllDeleteOrphan(),我不确定您是否需要 item.Order = null,对吗?这不是 AllDeleteOrphan() 所做的吗?其余的都在现场。犯了一个小学生的错误,即遍历物品并试图删除。
    • item.Order = null 是它成为孤儿的原因,否则会在刷新会话时重新保存。
    • 实际上您的代码不起作用。你仍然会得到一个异常:“Collection was modified;枚举操作可能无法执行。”我最终使用了一个 for 循环。
    • ToArray 应该将元素复制到一个新数组并防止您看到的错误。
    【解决方案2】:

    您始终可以创建自己的 RemoveAll 扩展方法:

    public static void RemoveAll<T>(this IList<T> source, Predicate<T> predicate)
    {
        // TODO: Argument non-nullity validation
    
        // Optimization
        List<T> list = source as List<T>
        if (list != null)
        {
            list.RemoveAll(predicate);
            return;
        }
    
        // Slow way
        for (int i = source.Count - 1; i >= 0; i--)
        {
            if (predicate(source[i]))
            {
                source.RemoveAt(i);
            }
        }
    }
    

    期望 NHibernate 关心集合本身 - 只是从 List&lt;T&gt; 中删除一个项目不是 NHibernate 可以“注意到”的事情。

    当然,如果您不希望 NHibernate 关心您所做的事情 - 如果您的更改只是意味着是本地的 - 那么您的最后一段代码就可以了。

    【讨论】:

    • 啊哈,所以标签“C#”吸引了伟大的 Jon Skeet,又名堆栈大师,他本人......很高兴知道 :)......我确实需要 NHibernate 知道关心这个操作.
    • @autonomatt:在这种情况下,假设返回的集合很乐意使用RemoveAt 删除项目,这应该没问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-02
    • 1970-01-01
    相关资源
    最近更新 更多