【问题标题】:Entity framework core update many to many实体框架核心更新多对多
【发布时间】:2023-04-11 12:42:02
【问题描述】:

我们正在将现有的 MVC6 EF6 应用程序移植到 Core。

EF Core 中是否有更新多对多关系的简单方法?

我在 EF6 中清除列表并用新数据覆盖它的旧代码不再有效。

var model = await _db.Products.FindAsync(vm.Product.ProductId);
model.Colors.Clear();
model.Colors =  _db.Colors.Where(x => 
vm.ColorsSelected.Contains(x.ColorId)).ToList();

【问题讨论】:

  • 不再工作是什么意思?
  • 它不再产生预期的结果。实体框架核心确实更改跟踪不同。
  • 我遇到了类似的问题,尝试了下面的各种答案,最后通过确保我在子集合上调用 Include()ThenInclude() 来修复它(在你的示例中为 Colors) .简单得多。这篇文章也很有用:thereformedprogrammer.net/…
  • 目前还不清楚您所说的“不再有效”是什么意思。如果关系的数量有限并且可以使用 EF 核心来完成此操作,那么仅替换关系是最简单的方法。

标签: entity-framework asp.net-core


【解决方案1】:

这对你有用。

创建一个类来建立关系:

public class ColorProduct
{
    public int ProductId { get; set; }
    public int ColorId { get; set; }

    public Color Color { get; set; }
    public Product Product { get; set; }
}

ColorProduct 集合添加到您的ProductColor 类中:

 public ICollection<ColorProduct> ColorProducts { get; set; }

然后使用我制作的这个扩展来删除未选择的并将新选择的添加到列表中:

public static void TryUpdateManyToMany<T, TKey>(this DbContext db, IEnumerable<T> currentItems, IEnumerable<T> newItems, Func<T, TKey> getKey) where T : class
{
    db.Set<T>().RemoveRange(currentItems.Except(newItems, getKey));
    db.Set<T>().AddRange(newItems.Except(currentItems, getKey));
}

public static IEnumerable<T> Except<T, TKey>(this IEnumerable<T> items, IEnumerable<T> other, Func<T, TKey> getKeyFunc)
{
    return items
        .GroupJoin(other, getKeyFunc, getKeyFunc, (item, tempItems) => new { item, tempItems })
        .SelectMany(t => t.tempItems.DefaultIfEmpty(), (t, temp) => new { t, temp })
        .Where(t => ReferenceEquals(null, t.temp) || t.temp.Equals(default(T)))
        .Select(t => t.t.item);
}

使用它看起来像这样:

var model = _db.Products
    .Include(x => x.ColorProducts)
    .FirstOrDefault(x => x.ProductId == vm.Product.ProductId);

_db.TryUpdateManyToMany(model.ColorProducts, vm.ColorsSelected
    .Select(x => new ColorProduct
    {
        ColorId = x,
        ProductId = vm.Product.ProductId
    }), x => x.ColorId);

【讨论】:

  • 哇,这太棒了。我可以在我的所有项目中使用这个扩展。感谢您的及时答复。
  • 这个解决方案的关键部分(以及 OP 代码中的问题)是 Include 调用。
  • AddRange 没有 Func getKey 参数
  • 是不是又好又有效的方法?或者你有没有找到其他可能的方法?
  • 为什么要检查 null 和 default 的相等性?我猜只有默认值就足够了(因为 ref 类型的默认值可以为 null)。
【解决方案2】:

为了避免上述答案中的 LINQ 地狱,可以将模板化的“Except”方法重写为:

public static IEnumerable<TEntity> LeftComplementRight<TEntity, TKey>(
        this IEnumerable<TEntity> left,
        IEnumerable<TEntity> right,
        Func<TEntity, TKey> keyRetrievalFunction)
    {
        var leftSet = left.ToList();
        var rightSet = right.ToList();

        var leftSetKeys = leftSet.Select(keyRetrievalFunction);
        var rightSetKeys = rightSet.Select(keyRetrievalFunction);

        var deltaKeys = leftSetKeys.Except(rightSetKeys);
        var leftComplementRightSet = leftSet.Where(i => deltaKeys.Contains(keyRetrievalFunction.Invoke(i)));
        return leftComplementRightSet;
    }

此外,可以更新 UpdateManyToMany 方法以包含已被修改的实体:

public void UpdateManyToMany<TDependentEntity, TKey>(
        IEnumerable<TDependentEntity> dbEntries,
        IEnumerable<TDependentEntity> updatedEntries,
        Func<TDependentEntity, TKey> keyRetrievalFunction)
        where TDependentEntity : class
    {
        var oldItems = dbEntries.ToList();
        var newItems = updatedEntries.ToList();
        var toBeRemoved = oldItems.LeftComplementRight(newItems, keyRetrievalFunction);
        var toBeAdded = newItems.LeftComplementRight(oldItems, keyRetrievalFunction);
        var toBeUpdated = oldItems.Intersect(newItems, keyRetrievalFunction);

        this.Context.Set<TDependentEntity>().RemoveRange(toBeRemoved);
        this.Context.Set<TDependentEntity>().AddRange(toBeAdded);
        foreach (var entity in toBeUpdated)
        {
            var changed = newItems.Single(i => keyRetrievalFunction.Invoke(i).Equals(keyRetrievalFunction.Invoke(entity)));
            this.Context.Entry(entity).CurrentValues.SetValues(changed);
        }
    }

它使用另一个自定义模板“相交”函数来查找两组的交集:

public static IEnumerable<TEntity> Intersect<TEntity, TKey>(
        this IEnumerable<TEntity> left,
        IEnumerable<TEntity> right,
        Func<TEntity, TKey> keyRetrievalFunction)
    {
        var leftSet = left.ToList();
        var rightSet = right.ToList();

        var leftSetKeys = leftSet.Select(keyRetrievalFunction);
        var rightSetKeys = rightSet.Select(keyRetrievalFunction);

        var intersectKeys = leftSetKeys.Intersect(rightSetKeys);
        var intersectionEntities = leftSet.Where(i => intersectKeys.Contains(keyRetrievalFunction.Invoke(i)));
        return intersectionEntities;
    }

【讨论】:

    猜你喜欢
    • 2020-01-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-15
    • 1970-01-01
    • 1970-01-01
    • 2019-03-04
    • 2019-05-06
    相关资源
    最近更新 更多