【问题标题】:EF Update Many-to-Many in Detached ScenarioEF 更新分离场景中的多对多
【发布时间】:2015-06-16 16:10:41
【问题描述】:

我试图创建一个通用方法来更新一个实体及其所有来自分离对象的集合属性。例如:

public class Parent{
    public int Id { get; set; }
    public string ParentProperty { get; set; }
    public List<Child> Children1 { get; set; }
    public List<Child> Children2 { get; set; }
}

public class Child{
    public int Id { get; set; }
    public string ChildProperty { get; set; }
}

所以,我的第一个意图是使用这样的东西:

Repository<Parent>.Update(parentObj);

在这个方法中拥有一个魔法来更新父属性并将 parentObj 的子列表与数据库中的当前值进行比较并相应地添加/更新/删除它们是完美的,但对于我对 EF 的了解来说,这太复杂了/Reflection/Generic... 所以我尝试了第二种更简单的方法,如下所示:

Repository<Parent>.Update(parentObj, parent => parent.Children1
                                     parent => parent.Children2);

这种方法使用起来会有点困难,但还是可以接受的。但是我如何认为第二个参数必须是params Expression&lt;Func&lt;TEntity, ICollection&lt;TRelatedEntity&gt;&gt;&gt;[] relatedEntities 我在指定多个 TRelatedEntity 时遇到了问题。所以我的尝试是第三步,但还没有成功......

现在我尝试调用一个方法来更新 Parent 和一系列方法来更新 Childreen,如下所示:

Repository<Parent>.Update(parentObj);
Repository<Parent>.UpdateChild(parentObj, parent => parent.Id, parent => parent.Children1);
Repository<Parent>.UpdateChild(parentObj, parent => parent.Id, parent => parent.Children2);

还有代码:

public virtual void Update(TEntity entityToUpdate)
{
    context.Entry(entityToUpdate).State = EntityState.Modified;
}

public virtual void UpdateChild<TRelatedEntity>(TEntity entityToUpdate, Func<TEntity, object> keySelector, Expression<Func<TEntity, ICollection<TRelatedEntity>>> relatedEntitySelector) where TRelatedEntity: class
{
    var entityInDb = dbSet.Find(keySelector.Invoke(entityToUpdate));

    var current = relatedEntitySelector.Compile().Invoke(entityToUpdate);
    var original = relatedEntitySelector.Compile().Invoke(entityInDb);

    foreach (var created in current.Except(original))
    {
        context.Set<TRelatedEntity>().Add(created);
    }

    foreach (var removed in original.Except(current))
    {
        context.Set<TRelatedEntity>().Remove(removed);
    }

    foreach (var updated in current.Intersect(original))
    {
        context.Entry(updated).State = EntityState.Modified;
    }

    context.Entry(entityInDb).State = EntityState.Detached;
}

第一个问题是获取原始值,因为当我调用 dbSet.Find 时,实体已经在上下文中 (context.Entry(entityToUpdate).State = EntityState.Modified;)。

所以我尝试改变调用第一个孩子的顺序:

Repository<Parent>.Update(parentObj);
Repository<Parent>.UpdateChild(parentObj, parent => parent.Id, parent => parent.Children1);
Repository<Parent>.UpdateChild(parentObj, parent => parent.Id, parent => parent.Children2);

现在我有错误:

存储更新、插入或删除语句影响了意外数量的行 (0)。自加载实体后,实体可能已被修改或删除。有关理解和处理乐观并发异常的信息,请参阅 http://go.microsoft.com/fwlink/?LinkId=472540

总而言之,第一种方式会很好,但我对第二种/第三种方式也很满意。

非常感谢

编辑 1

请,我需要一个本机解决方案或使用 Automapper(我们已经在项目中使用),因为我的客户不喜欢外部依赖项,如果我们需要对项目进行一些调整,例如使用附加对象进行更新它们的相关实体,因此 cmets 中提到的 GraphDiff 不符合我们的需求(当我尝试安装软件包进行测试时,VS 2015 RC 崩溃了)

【问题讨论】:

  • 你可能喜欢 GraphDiff。
  • @GertArnold 我正在阅读,如果这解决了我的问题,那么您可以发送给我作为答案,我会接受。 Tks
  • 你不应该标记为重复,因为“原始”问题是关于 GraphDiff 而我的不是。您可以将该问题称为答案。
  • 好的,请取消标记为重复,因为我需要本机解决方案,好吗? Tks

标签: c# entity-framework


【解决方案1】:

您是否考虑过从数据库中获取对象并使用 AutoMapper 修改所有属性值?

我的意思是:

var obj = GetObjectFromDB(...); 
AutoMapObj(obj, modifiedObj); 
SaveInDb();

【讨论】:

    猜你喜欢
    • 2014-07-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-16
    • 1970-01-01
    • 1970-01-01
    • 2011-07-10
    • 2012-10-04
    相关资源
    最近更新 更多