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