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崩溃了)
您是否考虑过从数据库获取对象并使用自动映射器修改所有属性值?
我的意思是:
var obj = GetObjectFromDB(...);
AutoMapObj(obj, modifiedObj);
SaveInDb();