且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

使用实体框架的最小存储库实现

更新时间:2022-11-04 21:41:04

首先,所有存储库都存在争议。有很多人强烈反对,并且出于各种原因有很多人使用它(或习惯了?)。互联网上有许多文章,都对利弊进行了无休止的讨论。由您决定是否在项目中确实需要存储库模式-当您询问如何在C#中执行此操作?时,请不要着重讨论该模式。不是我应该这样做吗?。

First of all repositories are controversial. There are lots of people strongly against and lots using it (or used to it?) for various reasons. There many articles over the internet with endless discussions about pros and cons. It's up to you to decide if you actually need the repository pattern in your project - let's not focus on that as you asked "how to do that in C#?" not "should i do that?".

您的存储库实现扩展了 DbContext 。这意味着您无法有效地创建一个跨越一个以上存储库(一种以上实体类型)的事务,因为每个存储库都将拥有其自己的 DbContext (因为它是上下文)。 DbContext 可以跟踪对实体所做的更改。如果您有多个上下文,并且尝试同时保存两个上下文,它们将不会彼此了解。这给我们带来了一个问题-如果 SaveChanges()的第一次调用成功,而第二次调用失败,如何回滚第一个?它已经保存了吗?

Your repository implementation extends DbContext. This means you cannot effectively create a transaction spanning more than one repository (more than one entity type) because each repository will have it's own DbContext (as it IS the context). Under the hood DbContext tracks changes made to the entities. If you have more than one context and try to save both at the same time they won't know about each other. This leaves us with a problem - if the first call of SaveChanges() succeeds and second fails how to rollback the first one? It was already saved?. That's where the unit of work appears.

所以首先需要一个存储库接口-充当实体的集合:

So first you need a repository interface - acting as a collection of entities:

public interface IRepository<TEntity>
{
    TEntity Get(Expression<Func<TEntity, bool>> predicate);
    IEnumerable<TEntity> GetAll();
    IEnumerable<TEntity> GetAll(Expression<Func<TEntity, bool>> predicate);

    void Add(TEntity entity);
    void AddAll(IEnumerable<TEntity> entities);

    void Remove(TEntity entity);
    void RemoveAll(IEnumerable<TEntity> entities);
}

工作单位:

public interface IUnitOfWork : IDisposable
{
    // Commit all the changes 
    void Complete();

    // Concrete implementation -> IRepository<Foo>
    // Add all your repositories here:
    IFooRepository Foos {get;}
}

基类如下:

public class BaseRepository<TEntity> : IRepository<TEntity> where TEntity : class
{
    protected DbContext Context { get; private set; }

    public BaseRepository(DbContext dbContext)
    {
        Context = dbContext;
    }

    public virtual TEntity Get(Expression<Func<TEntity, bool>> predicate)
    {
        return Context.Set<TEntity>().Where(predicate).FirstOrDefault();
    }

    public virtual IEnumerable<TEntity> GetAll()
    {
        return Context.Set<TEntity>().ToList();
    }

    public virtual IEnumerable<TEntity> GetAll(Expression<Func<TEntity, bool>> predicate)
    {
        return Context.Set<TEntity>().Where(predicate).ToList();
    }

    public void Add(TEntity entity)
    {
        var entry = Context.Entry(entity);
        if(entry.State == EntityState.Detached)
        {
            Context.Set<TEntity>().Add(entity);
        }
        else
        {
            entry.State = EntityState.Modified;
        }
    }

    public void AddAll(IEnumerable<TEntity> entities)
    {
        foreach(var entity in entities)
        {
            Add(entity);
        }
    }

    public void Remove(TEntity entity)
    {
        var entry = Context.Entry(entity);
        if (entry.State == EntityState.Detached)
        {
            Context.Set<TEntity>().Attach(entity);
        }
        Context.Entry<TEntity>(entity).State = EntityState.Deleted;
    }

    public void RemoveAll(IEnumerable<TEntity> entities)
    {
        foreach (var entity in entities)
        {
            Remove(entity);
        }
    }

}

和单位工作实现的实现:

And Unit of work implementation:

public class UnitOfWork : IUnitOfWork
{
    private readonly ApplicationDbContext _dbContext;
    private IFooRepository _fooRepo;

    public UnitOfWork(ApplicationDbContext dbContext)
    {
        _dbContext = dbContext;

        // Each repo will share the db context:
        _fooRepo = new FooRepository(_dbContext);
    }


    public IFooRepository Foos
    {
        get
        {
            return _fooRepo;
        }
    }

    public void Complete()
    {
        _dbContext.SaveChanges();
    }

    public void Dispose()
    {
        _dbContext.Dispose();
    }
}