具有 EntityFramework 的通用存储库

Posted

技术标签:

【中文标题】具有 EntityFramework 的通用存储库【英文标题】:Generic Repository with EntityFramework 【发布时间】:2017-08-07 10:45:53 【问题描述】:

我想使用实体框架实现一个通用的存储库模式(我知道关于存储库有很多有争议的观点,但这仍然是我需要的)。 我想要的界面如下:

public interface IRepository

    IQueryable<TEntity> Query<TEntity>() 
        where TEntity: Entity;

    void Save<TEntity>(TEntity entity) 
        where TEntity : Entity;

    void Delete<TEntity>(TEntity entity) 
        where TEntity : Entity;

Entity 是一个只有int ID 属性的基类。 并像这样使用它:

        IRepository repository = ... // get repository (connects to DB)
        int userId = GetCurrentUserId();
        if (!repository.Query<User>().Any(u => u.Id == userId)) // performs SELECT query
            /*return error*/    

        var newOrder = new Order  UserId = userId, Status = "New" 
        repository.Save(newOrder); // performs INSERT query
        ...
        newOrder.Status = "Completed";
        repository.Save(newOrder); // performs UPDATE query

我想避免UnitOwWork,只要调用Save()Delete() 就将所有对象更改提交到数据库。我想做的事情看起来很简单,但我没有找到任何使用 EntityFramework 的示例。我能找到的最接近的示例是 this answer,但它使用 UnitOwWork 和 repository-per-entity,其中比我需要做的更复杂。

【问题讨论】:

如果你想保持简单,那么不要在实体框架中使用存储库模式。实体框架本身已经是一个存储库模式,您只是在现有抽象之上添加更多抽象,它没有任何好处。相反,它可能会使您的代码更难维护和使用,同时增加开发额外不必要层的成本。 这不是一个通用存储库,它只是一个 DbContext 的包装器。我很好奇您是如何实现Save 的,因为它表明您可以有选择地保存一个实体,即使上下文包含更多已更改的实体。难以置信。如果可以的话,这违背了SaveChanges 方法的目的。 @GertArnold,接口一个存储库。我已经使用纯 ADO.NET 和 Dapper 实现了其中的一部分。 (顺便说一句,这是我想要对 EF 进行抽象的原因之一:我的应用程序的某些部分包含 EF 不完全支持的复杂查询,但我希望使用一个抽象来执行所有查询:IRepository)IRepository 不知道或关心 EF 或 DbContext。它应该是所有数据操作的通用简单接口(这在后台可能非常复杂 - 就像为不同的实体类型使用不同的 ORM)。 @Nazz IQueryable 将使用带有实现(f.i. EF)的 IRepository 接口“感染”您的代码。它不再那么抽象了 @Nazz 所以你是说在你的存储库抽象背后你可以有 EF 或 Dapper? 【参考方案1】:

1-创建一个接口

interface IMain<T> where T : class
    
        List<T> GetAll();
        T GetById(int id);
        void Add(T entity);
        void Edit(T entity);
        void Del(int id);
        int Savechange();
    

2-创建一个类

public class Main<T> : IMain<T> where T : class
    
        public DataContext db;
        public void Add(T entity)
        
            db.Set<T>().Add(entity);
        

        public void Del(int id)
        
            var q = GetById(id);
            db.Set<T>().Remove(q);
        

        public void Edit(T entity)
        
            db.Entry<T>(entity).State = EntityState.Modified;
        

        public List<T> GetAll()
        
            return db.Set<T>().Select(a=>a).ToList();
        

        public T GetById(int id)
        
            return db.Set<T>().Find(id);
        

        public int Savechange()
        
            return db.SaveChanges();
        
    

3-创建一个名为 YourTable ForExample Student

的存储库
 public class Student : Main<Tbl_Student>
    
        public Student()
        
            db = new DataContext();
        
    

4-为您的行动编写此代码

Student student=new Student();
student.Del(3);
int a = student.Savechange();

【讨论】:

【参考方案2】:

您可以使用表达式关键字来做到这一点;

    public interface IRepository<TEntity> where TEntity : Entity
    
        IQueryable<TEntity> Query(Expression<Func<TEntity, bool>> predicate);

        void Save(TEntity entity);

        void Delete(TEntity entity);
    

    public abstract class EfRepository<T> : IRepository<T> where T : Entity
    
        private readonly DbContext _dbContext;
        protected readonly DbSet<T> _dbSet;
        public EfRepository(YourDbContextContext dbContext)
        
            _dbContext = dbContext;
            _dbSet = dbContext.Set<T>();
        

        public void Delete(T entity)
        
            if (entity == null) return;
            else
            
                DbEntityEntry dbEntityEntry = _dbContext.Entry(entity);

                if (dbEntityEntry.State != EntityState.Deleted)
                
                    dbEntityEntry.State = EntityState.Deleted;
                
                else
                
                    _dbSet.Attach(entity);
                    _dbSet.Remove(entity);
                    _dbContext.SaveChanges();
                
            
        

        public IQueryable<T> Query(Expression<Func<T, bool>> predicate)
        
            return _dbSet.Where(predicate);
        

        public void Save(T entity)
        
            if (entity.Id > 0)
            
                _dbSet.Attach(entity);
                _dbContext.Entry(entity).State = EntityState.Modified;
                _dbContext.SaveChanges();
            
            else
            
                _dbSet.Add(entity);
                _dbContext.SaveChanges();
            
        
    
    public class Entity
    
        public int Id  get; set; 
    

然后创建您的存储库;

     public interface IUserRepository : IRepository<User>
     
       //Also you can add here another methods according to your needs
     
     public class UserRepository : EfRepository<User>,IUserRepository
     
            public UserRepository(YourDbContext yourDbContext) : base(yourDbContext)
            

            
     

那就用吧;

IUserRepository _userRepository => Getit
//If there are entities according to your conditions, this will return them, then use it
_userRepository.Query(u => u.Id == userId);

【讨论】:

【参考方案3】:

我曾经使用它,但正如许多开发人员所说,它会增加代码的复杂性并可能导致问题:

我的interface IRepositoryBase的代码:

public interface IRepositoryBase<TEntity> where TEntity : class

    void Add(TEntity objModel);
    void AddRange(IEnumerable<TEntity> objModel);
    TEntity GetId(int id);
    Task<TEntity> GetIdAsync(int id);
    TEntity Get(Expression<Func<TEntity, bool>> predicate);
    Task<TEntity> GetAsync(Expression<Func<TEntity, bool>> predicate);
    IEnumerable<TEntity> GetList(Expression<Func<TEntity, bool>> predicate);
    Task<IEnumerable<TEntity>> GetListAsync(Expression<Func<TEntity, bool>> predicate);
    IEnumerable<TEntity> GetAll();
    Task<IEnumerable<TEntity>> GetAllAsync();
    int Count();
    Task<int> CountAsync();
    void Update(TEntity objModel);
    void Remove(TEntity objModel);
    void Dispose(); 

我的interface 在repsoitory RepositoryBase 上的实现代码:

public class RepositoryBase<TEntity> : IRepositoryBase<TEntity> where TEntity : class

    #region Fields

    protected readonly EntityContext _context = new EntityContext();

    #endregion

    #region Methods

    public void Add(TEntity objModel)
    
        _context.Set<TEntity>().Add(objModel);
        _context.SaveChanges();
    

    public void AddRange(IEnumerable<TEntity> objModel)
    
        _context.Set<TEntity>().AddRange(objModel);
        _context.SaveChanges();
    

    public TEntity GetId(int id)
    
        return _context.Set<TEntity>().Find(id);
    

    public async Task<TEntity> GetIdAsync(int id)
    
        return await _context.Set<TEntity>().FindAsync(id);
    

    public TEntity Get(Expression<Func<TEntity, bool>> predicate)
    
        return _context.Set<TEntity>().FirstOrDefault(predicate);
    

    public async Task<TEntity> GetAsync(Expression<Func<TEntity, bool>> predicate)
    
        return await _context.Set<TEntity>().FirstOrDefaultAsync(predicate);
    

    public IEnumerable<TEntity> GetList(Expression<Func<TEntity, bool>> predicate)
    
        return _context.Set<TEntity>().Where<TEntity>(predicate).ToList();
    

    public async Task<IEnumerable<TEntity>> GetListAsync(Expression<Func<TEntity, bool>> predicate)
    
        return await Task.Run(() =>
            _context.Set<TEntity>().Where<TEntity>(predicate));
    

    public IEnumerable<TEntity> GetAll()
    
        return _context.Set<TEntity>().ToList();
    

    public async Task<IEnumerable<TEntity>> GetAllAsync()
    
        return await Task.Run(() => _context.Set<TEntity>());
    

    public int Count()
    
        return _context.Set<TEntity>().Count();
    

    public async Task<int> CountAsync()
    
        return await _context.Set<TEntity>().CountAsync();
    

    public void Update(TEntity objModel)
    
        _context.Entry(objModel).State = EntityState.Modified;
        _context.SaveChanges();
    

    public void Remove(TEntity objModel)
    
        _context.Set<TEntity>().Remove(objModel);
        _context.SaveChanges();
    

    public void Dispose()
    
        _context.Dispose();
    

    #endregion

我的实体interface

public interface IMyEntityRepository : IRepositoryBase<MyEntity>

     //here you can place other implementations your repository doesn't have


public class MyEntityRepository : RepositoryBase<MyEntity>, IMyEntityRepository


如何调用(我用的是依赖注入):

public class MyServiceOrController

    #region Fields

    private readonly IMyEntityRepository _myEntityRepository;

    #endregion

    #region Constructors

    public MyServiceOrController(IMyEntityRepository myEntityRepository)
    
        _myEntityRepository = myEntityRepository;
    

    #endregion

    #region Methods

    public IList<MyEntity> TestGetAll()
    
        return _myEntityRepository.GetAll();
    

    #endregion

【讨论】:

以上是关于具有 EntityFramework 的通用存储库的主要内容,如果未能解决你的问题,请参考以下文章

带有 ThenIncludes 的通用存储库模式

通用存储库、工作单元、Unity 的架构问题

访问存储库中的 UnitOfWork 是不好的设计吗?

Entity Framework 6 异步操作和 TranscationScope

ASP.NET MVC,EntityFramework,DBContext,不同项目中的存储库[关闭]

与所有存储库/服务类共享我的dbContext吗?