I have created a generic Repository. At the moment I have a StaffRepository that inherits from my generic one (Repository). This works well, as my generic Repository has my CRUD operations and allows me to use these in my service aswell as the methods in my StaffRepository which does additional filtering on the context and adds includes. However, I need to create a Repository for each entity, (Most filter by ClientId and the primary key). Is there a way I can use a generic approach for both the filtering and applying the includes? Also am I applying the right approach here? Thanks for your help.
Example StaffRepository:
public class StaffRepository : Repository<Staff>
{
public StaffRepository(ApplicationDbContext _context) : base(_context)
{
}
public async Task<List<Staff>> GetAsync(int clientId)
{
return await _context.Staff
.Include(s => s.Person)
.Include(u => u.Person.User)
.Include(p => p.Photograph)
.Where(x => x.ClientId == clientId)
.ToListAsync();
}
public async Task<Staff> GetByIdAsync(int clientId, int staffId)
{
return await _context.Staff
.Include(s => s.Person)
.Include(u => u.Person.User)
.FirstOrDefaultAsync(x => x.ClientId == clientId && x.StaffId == staffId);
}
}
My Generic Repository:
public class Repository<T> : IRepository<T> where T : class, new()
{
internal readonly ApplicationDbContext _context;
public Repository(ApplicationDbContext context)
{
this._context = context;
}
public async Task<T> FindAsync(int id)
{
return await _context.Set<T>().FindAsync(id);
}
public async Task InsertAsync(T entity)
{
await _context.Set<T>().AddAsync(entity);
await SaveAsync();
}
public async Task AddRangeAsync(IEnumerable<T> entities)
{
await _context.Set<T>().AddRangeAsync(entities);
await SaveAsync();
}
public async Task UpdateAsync(T entity)
{
_context.Set<T>().Attach(entity);
_context.Entry(entity).State = EntityState.Modified;
await SaveAsync();
}
public async Task DeleteAsync(T entity)
{
if (_context.Entry(entity).State == EntityState.Detached)
_context.Set<T>().Attach(entity);
_context.Set<T>().Remove(entity);
await SaveAsync();
}
public async Task SaveAsync()
{
await _context.SaveChangesAsync();
}
}
All you need to do is using expressions like you can see on my GetAsync method below:
public class GenericRepository<TEntity> where TEntity : class
{
internal DbContext dbContext;
internal DbSet<TEntity> dbSet;
public GenericRepository(DbContext context)
{
this.dbContext = context;
this.dbSet = context.Set<TEntity>();
}
public virtual async Task<IEnumerable<TEntity>> GetAsync(
Expression<Func<TEntity, bool>> filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>> orderBy = null,
string includeProperties = "",
int first = 0, int offset = 0)
{
IQueryable<TEntity> query = dbSet;
if (filter != null)
{
query = query.Where(filter);
}
if (offset > 0)
{
query = query.Skip(offset);
}
if (first > 0)
{
query = query.Take(first);
}
foreach (var includeProperty in includeProperties.Split
(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}
if (orderBy != null)
{
return await orderBy(query).ToListAsync();
}
else
{
return await query.ToListAsync();
}
}
}
I just included my repository with the GetAsync method and I will recommend you using the UnityOfWork pattern so you can have your instances of this repository (for each entity) on a centralized place like:
public class UnitOfWork : IDisposable
{
private GenericRepository<YourEntity> yourRepository;
private DbContext context;
public UnitOfWork(DbContext context)
{
this.context = context;
}
public GenericRepository<YourEntity> YourRepository
{
get
{
if (this.yourRepository== null)
{
this.yourRepository= new GenericRepository<YourEntity>(context);
}
return yourRepository;
}
}
}
Then this is just an example how to instantiate this:
await unitOfWork.YourRepository.GetAsync(filter: w => w.OtherKeyNavigation.Id == 123456, includeProperties: "OtherKeyNavigation", first: 20, offset: 0);
Note: I'm not including all the code from neither the Repository nor the UnityOfWork because is not part of this question. Thanks
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With