Does anyone have a very complete example generic r

2019-02-01 14:41发布

问题:

I have my own repository that is as follows. However this does not take into account some of the new features such as the range features. Does anyone have a repository that includes everything. I have searched for this on the net but there's nothing that I can find that is recent. Here is what I have. I am hoping for something that has more and that offers IQueryable for many of the methods:

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);
        }
    }
}

回答1:

You can add new features like this:

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:

You don't need a generic repository. DbContext already is a generic repository. Try this out:

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);
    }
}

... and the interfaces are merely a formality if you need to separate UoW from queries from commands:

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);
}

With this, your interface doesn't need to be generic because the methods are generic.

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

etc. You just saved 3 unnecessary generic repository dependencies in the code above. One interface can handle all 4 of the entitiy types.



回答3:

Take a look at Generic Unit of Work & Repositories Framework. You can download the entire repository project, modify the code to include the range functions, implement it in your own solution, etc.

Here's an example of its usage in the context of an OData/WebAPI controller method returning a DTO, rather than the EF entity.

            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
                    }
                });

Hope that helps.