This short post (or rather code block) is just a slightly adapted generic design pattern from MSDN without Unit of Work pattern implementation. I've adapted it because of the requirement for asynchronous APIs for a single Context. I post it primarily to me personally for later use - to find it quickly, but if you find it useful too, I'm glad. If you have any suggestions for further improvements, please comment.
Here is the code snippet:
class Repository<TEntity> : IDisposable where TEntity : class
{
private bool isDisposed;
private ILogger<Repository<TEntity>> Logger { get; }
private ApplicationDbContext Context { get; }
private DbSet<TEntity> DbSet { get; }
public Repository(
ILogger<Repository<TEntity>> logger,
ApplicationDbContext context)
{
Logger = logger ?? throw new ArgumentNullException(nameof(logger));
Context = context ?? throw new ArgumentNullException(nameof(context));
DbSet = Context.Set<TEntity>();
}
public virtual Task<List<TEntity>> GetAsync(
Expression<Func<TEntity, bool>>? filter = null,
Func<IQueryable<TEntity>, IOrderedQueryable<TEntity>>? orderBy = null,
string includeProperties = "")
{
IQueryable<TEntity> query = DbSet;
if (filter != null)
{
query = query.Where(filter);
}
foreach (var includeProperty in includeProperties.Split(",", StringSplitOptions.RemoveEmptyEntries))
{
query = query.Include(includeProperty);
}
if (orderBy != null)
{
return orderBy(query).ToListAsync();
}
else
{
return query.ToListAsync();
}
}
public virtual ValueTask<TEntity?> GetByIdAsync(object id, CancellationToken cancellationToken = default) =>
DbSet.FindAsync(id, cancellationToken);
public virtual ValueTask<EntityEntry<TEntity>> InsertAsync(TEntity entity, CancellationToken cancellationToken = default) =>
DbSet.AddAsync(entity, cancellationToken);
public virtual async Task DeleteAsync(object id, CancellationToken cancellationToken = default)
{
var entityToDelete = await DbSet.FindAsync(id, cancellationToken);
if (entityToDelete != null) Delete(entityToDelete);
}
public virtual void Delete(TEntity entityToDelete)
{
if (Context.Entry(entityToDelete).State == EntityState.Detached)
{
DbSet.Attach(entityToDelete);
}
DbSet.Remove(entityToDelete);
}
public virtual void Update(TEntity entityToUpdate)
{
DbSet.Attach(entityToUpdate);
Context.Entry(entityToUpdate).State = EntityState.Modified;
}
public virtual async Task SaveChangesAsync(CancellationToken cancellationToken = default) =>
await Context.SaveChangesAsync();
protected virtual void Dispose(bool disposing)
{
if (!isDisposed)
{
if (disposing)
{
Context.Dispose();
}
isDisposed = true;
}
}
public void Dispose()
{
Dispose(disposing: true);
GC.SuppressFinalize(this);
}
}