【问题标题】:Does anyone have a very complete example generic repository for EF 6.1?有没有人有一个非常完整的 EF 6.1 通用存储库示例?
【发布时间】:2014-02-05 17:17:02
【问题描述】:

我有自己的存储库,如下所示。但是,这并没有考虑到一些新功能,例如范围功能。有没有人有一个包含所有内容的存储库。我已经在网上搜索过这个,但我找不到任何最近的东西。这就是我所拥有的。我希望有更多的东西,并为许多方法提供 IQueryable:

namespace Services.Repositories
{
    /// <summary>
    /// The EF-dependent, generic repository for data access
    /// </summary>
    /// <typeparam name="T">Type of entity for this Repository.</typeparam>
    public class GenericRepository<T> : IRepository<T> where T : class
    {
        public GenericRepository(DbContext dbContext)
        {
            if (dbContext == null) 
                throw new ArgumentNullException("An instance of DbContext is required to use this repository", "context");
            DbContext = dbContext;
            DbSet = DbContext.Set<T>();
        }

        protected DbContext DbContext { get; set; }

        protected DbSet<T> DbSet { get; set; }

        public virtual IQueryable<T> Find(Expression<Func<T, bool>> predicate)
        {
            return DbSet.Where<T>(predicate);
        }

        public virtual IQueryable<T> GetAll()
        {
            return DbSet;
        }

        public virtual T GetById(int id)
        {
            //return DbSet.FirstOrDefault(PredicateBuilder.GetByIdPredicate<T>(id));
            return DbSet.Find(id);
        }

        public virtual void Add(T entity)
        {
            DbEntityEntry dbEntityEntry = DbContext.Entry(entity);
            if (dbEntityEntry.State != EntityState.Detached)
            {
                dbEntityEntry.State = EntityState.Added;
            }
            else
            {
                DbSet.Add(entity);
            }
        }

        public virtual void Update(T entity)
        {
            DbEntityEntry dbEntityEntry = DbContext.Entry(entity);
            if (dbEntityEntry.State == EntityState.Detached)
            {
                DbSet.Attach(entity);
            }  
            dbEntityEntry.State = EntityState.Modified;
        }

        public virtual void Delete(T entity)
        {
            DbEntityEntry dbEntityEntry = DbContext.Entry(entity);
            if (dbEntityEntry.State != EntityState.Deleted)
            {
                dbEntityEntry.State = EntityState.Deleted;
            }
            else
            {
                DbSet.Attach(entity);
                DbSet.Remove(entity);
            }
        }

        public virtual void Delete(int id)
        {
            var entity = GetById(id);
            if (entity == null) return; // not found; assume already deleted.
            Delete(entity);
        }
    }
}

【问题讨论】:

  • 请不要使用通用存储库。当已经使用像实体框架这样的抽象时,这是一个非常糟糕的模式。你将一无所获,而且实际上会损害你以后优化的能力。朋友不要让朋友使用存储库模式。

标签: entity-framework entity-framework-6


【解决方案1】:

您可以像这样添加新功能:

public virtual void AddRange(IEnumerable<T> entities)
{
    DbContext.Set<T>().AddRange(entities);
}

public virtual void RemoveRange(IEnumerable<T> entities)
{
    DbContext.Set<T>().RemoveRange(entities);
}

【讨论】:

    【解决方案2】:

    您不需要通用存储库。 DbContext 已经一个通用存储库。试试这个:

    public class EntityDbContext : DbContext, IWriteEntities
    {
        public IQueryable<TEntity> EagerLoad<TEntity>(IQueryable<TEntity> query,
            Expression<Func<TEntity, object>> expression)
        {
            // Include will eager load data into the query
            if (query != null && expression != null)
                query = query.Include(expression);
            return query;
        }
    
        public IQueryable<TEntity> Query<TEntity>()
        {
            // AsNoTracking returns entities that are not attached to the DbContext
            return Set<TEntity>().AsNoTracking();
        }
    
        public TEntity Get<TEntity>(object firstKeyValue, params object[] otherKeyValues)
        {
            if (firstKeyValue == null) throw new ArgumentNullException("firstKeyValue");
            var keyValues = new List<object> { firstKeyValue };
            if (otherKeyValues != null) keyValues.AddRange(otherKeyValues);
            return Set<TEntity>().Find(keyValues.ToArray());
        }
    
        public Task<TEntity> GetAsync<TEntity>(object firstKeyValue, params object[] otherKeyValues)
        {
            if (firstKeyValue == null) throw new ArgumentNullException("firstKeyValue");
            var keyValues = new List<object> { firstKeyValue };
            if (otherKeyValues != null) keyValues.AddRange(otherKeyValues);
            return Set<TEntity>().FindAsync(keyValues.ToArray());
        }
    
        public IQueryable<TEntity> Get<TEntity>()
        {
            return Set<TEntity>();
        }
    
        public void Create<TEntity>(TEntity entity)
        {
            if (Entry(entity).State == EntityState.Detached)
                Set<TEntity>().Add(entity);
        }
    
        public void Update<TEntity>(TEntity entity)
        {
            var entry = Entry(entity);
            entry.State = EntityState.Modified;
        }
    
        public void Delete<TEntity>(TEntity entity)
        {
            if (Entry(entity).State != EntityState.Deleted)
                Set<TEntity>().Remove(entity);
        }
    
        public void Reload<TEntity>(TEntity entity)
        {
            Entry(entity).Reload();
        }
    
        public Task ReloadAsync<TEntity>(TEntity entity)
        {
            return Entry(entity).ReloadAsync();
        }
    
        public void DiscardChanges()
        {
            foreach (var entry in ChangeTracker.Entries().Where(x => x != null))
            {
                switch (entry.State)
                {
                    case EntityState.Added:
                        entry.State = EntityState.Detached;
                        break;
                    case EntityState.Modified:
                        entry.State = EntityState.Unchanged;
                        break;
                    case EntityState.Deleted:
                        entry.Reload();
                        break;
                }
            }
        }
    
        public Task DiscardChangesAsync()
        {
            var reloadTasks = new List<Task>();
            foreach (var entry in ChangeTracker.Entries().Where(x => x != null))
            {
                switch (entry.State)
                {
                    case EntityState.Added:
                        entry.State = EntityState.Detached;
                        break;
                    case EntityState.Modified:
                        entry.State = EntityState.Unchanged;
                        break;
                    case EntityState.Deleted:
                        reloadTasks.Add(entry.ReloadAsync());
                        break;
                }
            }
            return Task.WhenAll(reloadTasks);
        }
    }
    

    ...如果您需要将 UoW 与查询和命令分开,那么接口只是一种形式:

    public interface IUnitOfWork
    {
        int SaveChanges();
        Task<int> SaveChangesAsync();
        Task DiscardChangesAsync();
        void DiscardChanges();
    }
    
    public interface IReadEntities
    {
        IQueryable<TEntity> Query<TEntity>();
    
        IQueryable<TEntity> EagerLoad<TEntity>(IQueryable<TEntity> query, 
            Expression<Func<TEntity, object>> expression);
    }
    
    public interface IWriteEntities : IUnitOfWork, IReadEntities
    {
        TEntity Get<TEntity>(object firstKeyValue, params object[] otherKeyValues);
        Task<TEntity> GetAsync<TEntity>(object firstKeyValue,
            params object[] otherKeyValues);
        IQueryable<TEntity> Get<TEntity>();
        void Create<TEntity>(TEntity entity);
        void Delete<TEntity>(TEntity entity);
        void Update<TEntity>(TEntity entity);
        void Reload<TEntity>(TEntity entity);
        Task ReloadAsync<TEntity>(TEntity entity);
    }
    

    有了这个,你的界面不需要是通用的,因为方法是通用的。

    private readonly IWriteEntities _entities;
    ...
    _entities.Get<MyEntityA>(keyA);
    await _entities.GetAsync<MyEntityB>(keyB);
    _entities.Get<MyEntityC>.Where(...
    var results = await _entities.Query<MyEntityD>().SingleOrDefaultAsync(...
    

    等等。您刚刚在上面的代码中保存了 3 个不必要的通用存储库依赖项。一个接口可以处理所有 4 种实体类型。

    【讨论】:

    • 感谢您的建议。我认为这肯定是我们将来应该研究的事情。但是现在我们已经在使用通用存储库,因此我们希望使用 EF6.1 中提供的功能尽可能地更新它
    • 我不认为这些类型的 cmets 有帮助。当然,DbContext 可以作为“通用存储库”发挥作用,还有其他原因您希望将其抽象为您自己的。即能够使用泛型和 IoC 注入多个上下文,而无需将存储库实现耦合到上下文本身。
    • @Brandon,您的评论假设其他人都知道 DbContext 可以单独用作通用存储库。我相信这个答案很有帮助,因为它假设它们不是。此外,我认为许多(如果不是大多数)应用程序不使用多个上下文。当他们必须时,我同意将存储库实现与上下文分离是合适的。否则,我认为这是矫枉过正。
    • 对于上面的查询功能;该行:返回 Set().AsNoTracking();显示:错误 2 类型“TEntity”必须是引用类型才能将其用作泛型类型或方法“System.Data.Entity.DbContext.Set()”中的参数“TEntity”...是镇压野蛮的野兽需要一些参考资料吗?如果我将 'where TEntity : Client' 添加到签名的定义中,那很好。那么我是否需要所有 DB 对象类型的基类?
    • @RobertAchmann 虽然我确实建议为所有 EF 实体类实现实体超类型模式,但您是否尝试添加 where TEntity : class
    【解决方案3】:

    看看Generic Unit of Work & Repositories Framework。您可以下载整个存储库项目,修改代码以包含range 函数,在您自己的解决方案中实现它等等。

    这是一个在返回 DTO 而不是 EF 实体的 OData/WebAPI 控制器方法的上下文中使用它的示例。

                var results = odataQueryOptions.ApplyTo(_uow.Repository<ContentType>()
                    .Query()
                    .Get()
                    .Include(u => u.User)
                    .Where(u => u.UserId == userId)
                    .OrderBy(o => o.Description)).Cast<ContentType>()
                    .Select(x => new ContentTypeDTO()
                    {
                        //projection goes here
                        ContentTypeId = x.ContentTypeId,
                        Description = x.Description,
                        UserDTO = new UserDTO 
                        { 
                            UserId = x.UserId,
                            UserName = x.User.UserName
                        }
                    });
    

    希望对您有所帮助。

    【讨论】:

      猜你喜欢
      • 2012-10-25
      • 1970-01-01
      • 2011-11-19
      • 1970-01-01
      • 1970-01-01
      • 2015-02-27
      • 1970-01-01
      • 2012-04-21
      • 2015-03-22
      相关资源
      最近更新 更多