在分离场景中EF更新多对多

时间:2015-06-16 16:10:41

标签: c# entity-framework

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

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);

在这个方法中有一个神奇的东西可以更新Parent属性,并将parentObj的Children列表与数据库中的当前值进行比较,并相应地添加/更新/删除它们,但这对于我对EF的了解太复杂了/ Reflection / Generic ......所以我尝试了另一种更简单的方法:

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

这种方法使用起来会有点困难,但仍然可以接受。但我认为第二个参数必须是params Expression<Func<TEntity, ICollection<TRelatedEntity>>>[] 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(我们已在项目中使用),因为我的客户不喜欢外部依赖,如果我们需要适应项目,比如使用Attached对象来更新他们的相关实体,因此评论中的GraphDiff不符合我们的需求(当我尝试安装测试包时,VS 2015 RC崩溃了)

1 个答案:

答案 0 :(得分:1)

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

我的意思是:

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