【问题标题】:Very generic CreateOrUpdate method with Entity Framework具有实体框架的非常通用的 CreateOrUpdate 方法
【发布时间】:2013-08-19 16:56:27
【问题描述】:

我创建了一个通用存储库类,我的所有其他存储库类都从该类继承。这很棒,因为这意味着几乎所有的管道都为所有存储库完成了一次。我对我所说的 here 进行了完整的解释,但这里是我的 GenericRepository 的代码(为简洁起见,删除了一些代码):

public abstract class GenericRepository<T> : IGenericRepository<T> where T : class, new()
{
    private IMyDbContext _myDbContext;

    public GenericRepository(IMyDbContext myDbContext)
    {
        _myDbContext = myDbContext;
    }

    protected IMyDbContext Context
    {
        get
        {
            return _myDbContext;
        }
    }

    public IQueryable<T> AsQueryable()
    {
        IQueryable<T> query = Context.Set<T>();
        return query;
    }

    public virtual void Create(T entity)
    {
        Context.Set<T>().Add(entity);
    }

    public virtual void Update(T entity)
    {
        Context.Entry(entity).State = System.Data.EntityState.Modified;
    }
}

如您所见,我有一个 Create 方法和一个 Update 方法。有一个“CreateOrUpdate”方法会很方便,所以我不必每次必须将某些内容保存到数据库时手动检查现有对象。

我在 Entity Framework 中的每个对象都有一个“Id”,但这里的挑战是 GenericRepository 与“T”一起使用。

现在,通过相当长的介绍,我的具体问题。

如何为我的GenericRepository 创建一个通用的CreateOrUpdate 方法?

更新

在 Marcins 响应之后,我在我的 GenericRepository 中实现了以下通用方法。我需要一些时间才能测试它是否按预期工作,但它看起来很有希望。

public virtual bool Exists(Guid id)
{
    return Context.Set<T>().Any(t => t.Id == id);
}

public virtual void CreateOrUpdate(T entity)
{
    if (Exists(entity.Id))
    {
        var oldEntity = GetSingle(entity.Id);
        Context.Entry(oldEntity).CurrentValues.SetValues(entity);
        Update(oldEntity);
    }
    else
    {
        Create(entity);
    }
}

上述代码在更新时与数据库的往返次数不少于 3 次。我确信它可以被优化,但这并不是这个问题的真正练习。

这个问题更好地处理了这个话题: An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key

【问题讨论】:

  • 您访问数据库以确定是否需要插入或更新实体。您只需要检查实体的 Id 是否是 Guid 的默认值。 (entity.ID == default(Guid))。如果您打算使用延迟加载,您可能还需要确保始终创建代理。请参阅此示例:stackoverflow.com/a/16811976/150342
  • 某些业务逻辑可能希望在创建时尽早将 Guid 设置为主键,因此在我的情况下,我将不得不对数据库进行少量往返。否则,这是一个很好的观点,这在大多数其他情况下都是相关的。谢谢。
  • 实际上,每当我尝试更新An object with the same key already exists in the ObjectStateManager. The ObjectStateManager cannot track multiple objects with the same key. 时,我的实现都会失败并出现以下错误。当发送与现有对象具有相同 Guid 的新对象时会发生这种情况。它与这个问题无关,但只是想我会让你知道,以防其他人想使用这个代码。
  • 让调用者决定是否需要创建或更新有什么问题。为什么要将逻辑添加到 api/app/service 层?
  • 因为 UI 可能对它是创建还是更新没有意见,它只是保存一些信息,并且无论是创建还是更新都以相同的方式保存。所以它在前端节省了大量的逻辑工作,有一个单一的方法来处理这两者。可能还有其他原因。

标签: entity-framework repository-pattern


【解决方案1】:

使用Id 属性创建一个接口,在您的每个实体上实现它并向您的类添加另一个通用约束:

public interface IEntity
{
    int Id { get; set;}
}

public abstract class GenericRepository<T> : IGenericRepository<T> where T : class, IEntity, new()

这样,您就可以在通用存储库类中使用 Id 属性。

当然 - Id 不必是 int,也可以是 Guid

【讨论】:

  • 好主意,我试试看。顺便说一句,class 需要在IEntity 之前。
  • @NielsBrinch 好地方! MSDN 对此只字未提,但您是对的:structclass 约束必须在列表的首位。我已经编辑了我的问题。
  • 如果我想让我的 id 属性为 classNameId,比如 userId 或 clientId 怎么办?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-02-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-02-03
  • 2019-04-11
  • 1970-01-01
相关资源
最近更新 更多