Recently I worked on a project with ASP.NET Core which uses Entity Framework Core.
With them, I used a generic repository pattern in the data layer.
Repository interface was like below:
Repository interface was like below:
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using OnlineSurvey.Models;
namespace OnlineSurvey.Data
{
public interface IRepository<T> where T : BaseEntity
{
T GetById(int id, params Expression<Func<T, object>>[] includExpressions);
void Add(T entity);
void Delete(T entity);
void Delete(int id);
IEnumerable<T> GetAll(params Expression<Func<T, object>>[] includExpressions);
IEnumerable<T> Find(Expression<Func<T, bool>> where);
int Count();
}
}
using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using OnlineSurvey.Models;
namespace OnlineSurvey.Data
{
public class Repository<T> : IRepository<T> where T : BaseEntity
{
protected DbSet<T> DbSet => this.Context.Set<T>();
protected DataContext Context { get; set; }
public Repository(DataContext context)
{
this.Context = context ?? throw new ArgumentException("An instance of DbContext is required to use this repository.", nameof(context));
}
public IEnumerable<T> GetAll(params Expression<Func<T, object>>[] includExpressions)
{
if (includExpressions.Any())
{
return
includExpressions.Aggregate<Expression<Func<T, object>>, IQueryable<T>>(this.DbSet,
(current, expression) => current.Include(expression)).ToList();
}
return this.DbSet.ToList();
}
public T GetById(int id, params Expression<Func<T, object>>[] includExpressions)
{
if (includExpressions.Any())
{
var set =
includExpressions.Aggregate<Expression<Func<T, object>>, IQueryable<T>>(this.DbSet,
(current, expression) => current.Include(expression));
return set.SingleOrDefault(s => s.Id == id);
}
return this.DbSet.Find(id);
}
public void Add(T entity)
{
DbEntityEntry entry = this.Context.Entry(entity);
if (entry.State != EntityState.Detached)
{
entry.State = EntityState.Added;
}
else
{
this.DbSet.Add(entity);
}
}
public void Delete(T entity)
{
DbEntityEntry entry = this.Context.Entry(entity);
if (entry.State != EntityState.Deleted)
{
entry.State = EntityState.Deleted;
}
else
{
this.DbSet.Attach(entity);
this.DbSet.Remove(entity);
}
}
public void Delete(int id)
{
var entity = this.GetById(id);
if (entity != null)
{
this.Delete(entity);
}
}
public IEnumerable<T> Find(Expression<Func<T, bool>> where)
{
return DbSet.Where(where).ToList();
}
public int Count()
{
return DbSet.Count();
}
}
}
Usage of the above mentioned implementation:
Question question = this._unitOfWork.Questions.GetById(id, q=>q.Answers);
Above code has one issue. There is no way load entities inside a list. This problem can be fixed if we could have access to inbuilt function .ThenInclude.The following change will give us access to the above mentioned function.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using OnlineSurvey.Models;
using Microsoft.EntityFrameworkCore.Query;
namespace OnlineSurvey.Data
{
public interface IRepository<T> where T : BaseEntity
{
Task<T> GetByIdAsync(int id, Func<IQueryable<T>, IIncludableQueryable<T, object>> include = null);
Task AddAsync(T entity);
void Delete(T entity);
Task DeleteAsync(int id);
Task<IEnumerable<T>> GetAllAsync(Func<IQueryable<T>, IIncludableQueryable<T, object>> include = null);
Task<IEnumerable<T>> FindAsync(Expression<Func<T, bool>> predicate = null,
Func<IQueryable<T>, IIncludableQueryable<T, object>> include = null);
int Count();
Task<T> FirstOrDefaultAsync(Expression<Func<T, bool>> predicte = null,
Func<IQueryable<T>, IIncludableQueryable<T, object>> include = null);
void Update(T entity);
}
}
Refer to the below implementation: using System;
using System.Data.Entity;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using OnlineSurvey.Models;
using System.Threading.Tasks;
namespace OnlineSurvey.Data
{
public class Repository<T> : IRepository<T> where T : BaseEntity
{
protected DbSet<T> DbSet => this.Context.Set<T>();
protected DbContext Context { get; set; }
public Repository(DbContext context)
{
this.Context = context ?? throw new ArgumentException("An instance of DbContext is required to use this repository.", "context");
}
public async Task<T> GetByIdAsync(int id, Func<IQueryable<T>, IIncludableQueryable<T, object>> include = null)
{
IQueryable<T> query = this.DbSet;
if(include != null)
{
query = include(query);
}
return await query.AsNoTracking().SingleOrDefaultAsync(s => s.Id == id);
}
public async Task AddAsync(T entity)
{
EntityEntry entry = this.Context.Entry(entity);
if(entity.State != EntityState.Detached)
{
entry.State = EntityState.Added;
}
else
{
await this.DbSet.AddAsync(entity);
}
}
public void Delete(T entity)
{
EntityEntry entry = this.Context.Entry(entity);
if (entity.State != EntityState.Deleted)
{
entry.State = EntityState.Deleted;
}
else
{
this.DbSet.Attach(entity);
this.DbSet.Remove(entity);
}
}
public async Task DeleteAsync(int id)
{
T entity = await this.GetByIdAsync(id);
if(entity != null)
{
this.Delete(entity);
}
}
public async Task<IEnumerable<T>> GetAllAsync(Func<IQueryable<T>, IIncludableQueryable<T, object>> include = null)
{
IQueryable<T> query = this.DbSet;
if (include != null)
{
query = include(query);
}
return await query.AsNoTracking().ToListAsync();
}
public async Task<IEnumerable<T>> FindAsync(Expression<Func<T, bool>> predicate = null, Func<IQueryable<T>, IIncludableQueryable<T, object>> include = null)
{
IQueryable<T> query = this.DbSet;
if (include != null)
{
query = include(query);
}
if(predicate != null)
{
query = query.Where(predicate);
}
return await query.AsNoTracking().ToListAsync();
}
public int Count()
{
return this.DbSet.Count();
}
public async Task<T> FirstOrDefaultAsync(Expression<Func<T, bool>> predicate = null, Func<IQueryable<T>, IIncludableQueryable<T, object>> include = null)
{
IQueryable<T> query = this.DbSet;
if (include != null)
{
query = include(query);
}
if (predicate != null)
{
query = query.Where(predicate);
}
return await query.FirstOrDefaultAsync();
}
public void Update(T entity)
{
EntityEntry entry = this.Context.Entry(entity);
entity.State = EntityState.Modified;
}
}
}
Usage of the above-mentioned implementation: Question question = this._unitOfWork.Questions.GetById(id,
q=>q.Include(q => q.Answers).ThenInclude(a => a.Question));
Hope this helps you in some way.
Comments