通用实体框架存储库的更新方法

4

我有一个类似这样的代码库:

public class Repository<T> : IRepository<T> where T : class 
{
    private readonly IRepositoryContext _repositoryContext;

    public Repository(IRepositoryContext repositoryContext)
    {
        _repositoryContext = repositoryContext;
        _objectSet = repositoryContext.GetObjectSet<T>();
    }

    public virtual void Update(T entity)
    {
        ObjectSet.AddObject(entity);
        _repositoryContext.ObjectContext.ObjectStateManager.ChangeObjectState(entity, EntityState.Modified);
        _repositoryContext.SaveChanges();
    }
  }

现在,这适用于实体的所有标量属性,但与实体属性相关的所有其他实体并不关心实体状态被修改,EF只是添加新数据。
因此,如果您例如执行 Repository<Student>.Update(),并且只更改了名称,则它将找到正确的学生并更改其姓名,但它也将更改校区,尽管您已经有一个与该学生关联的校区,但将创建具有不同CampusId的新校区。
请展示正确的更新方法。

欢迎加入EF用户更新关系和相关实体的痛苦俱乐部。你可能不会找到一个通用的解决方案来更新方法。这也是为什么单独使用泛型存储库在EF中如此无用的主要原因之一(除非作为聚合存储库内部助手)。 - Slauma
2
没有“正确的方法”。在这种情况下,简单的通用方法行不通。 - Ladislav Mrnka
好的,那你能否展示一下非泛型方法,用于我不直接操作实体的情况吗?相反,我通过AutoMapper将它们映射到ViewModel类。 - iLemming
1
以下是更新具有单个子集合的分离实体的代码示例:https://dev59.com/6W035IYBdhLWcg3wPdcS。基本上,从数据库加载原始对象图形,与您更改的分离对象图形进行比较,将检测到的所有更改应用于原始并保存。它适用于DbContext,但ObjectContext的思想相同。对于更复杂的对象图(具有多个集合和孙子集合等),这变得更加复杂。可能还有其他方法,但可能没有更简单的方法。 - Slauma
1个回答

4

当我想要采用通用方法时,我将其翻译成了你的代码,大致如下:

public class Repository<T> : IRepository<T> where T : class 
{
    ...

    public virtual void Update(T entity)
    {
        if (context.ObjectStateManager.GetObjectStateEntry(entity).State == EntityState.Detached)
        {
            throw new InvalidOperationException(...);
        }

        _repositoryContext.SaveChanges();
    }
}

我的所有代码都像这样:

var attachedEntity = repository.Find(someId);
// Merge all changes into attached entity here
repository.Update(attachedEntity);

=> 以通用的方式实现这一点会将许多逻辑移动到上层。在处理大型分离对象图时(特别是涉及多对多关系和删除关系时),没有比这更好的方法了。


3
这个解决方案的问题在于它只考虑了更新操作,但对于插入新对象时也会出现类似的问题。如果你插入一个与数据库中其他已存在的分离实体相关联的对象(这些实体可能又与其他实体/子实体相关联),那么所有这些实体都将被重新创建。简直是一场噩梦。这种行为似乎是毫无必要的,特别是因为这些分离的对象都具有经过填充的ID属性,所以很容易检测到它们已经在先前的调用中被加载过了。 - Marchy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接