EF 在分离方案中更新多对多

本文关键字:更新 方案 分离 EF | 更新日期: 2023-09-27 17:56:47

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

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/反射/泛型的了解来说,这太复杂了......所以我尝试了第二种更简单的方法,如下所示:

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(我们已经在项目中使用),因为我的客户不喜欢外部依赖项,并且如果我们需要使某些内容适应项目,例如使用附加对象来更新其相关实体,因此注释中的GraphDiff不符合我们的需求(当我尝试安装软件包进行测试时,VS 2015 RC崩溃了)

EF 在分离方案中更新多对多

您是否考虑过从数据库获取对象并使用自动映射器修改所有属性值?

我的意思是:

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