Skip to content

Instantly share code, notes, and snippets.

@MongkonEiadon
Created June 27, 2018 12:07
Show Gist options
  • Save MongkonEiadon/ecc827969b0a522f38a8c69a9feb23b0 to your computer and use it in GitHub Desktop.
Save MongkonEiadon/ecc827969b0a522f38a8c69a9feb23b0 to your computer and use it in GitHub Desktop.
public class EfCoreRepositoryBase<TEntity> : EfCoreRepositoryBase<TEntity, Guid>, IRepository<TEntity>
where TEntity : class, IEntity
{
public EfCoreRepositoryBase(AiOptionContext dbDbContext) : base(dbDbContext)
{
}
}
public class EfCoreRepositoryBase<TEntity, TPrimaryKey> : RepositoryBase<TEntity, TPrimaryKey>, IRepositoryWithDbContext
where TEntity : class, IEntity<TPrimaryKey>
{
private readonly AiOptionContext _dbContext;
public virtual DbSet<TEntity> Table => _dbContext.Set<TEntity>();
public EfCoreRepositoryBase(AiOptionContext dbDbContext)
{
_dbContext = dbDbContext;
}
public override IQueryable<TEntity> GetAll()
{
return Table.AsQueryable();
}
public override TEntity Insert(TEntity entity)
{
var newEntity = Table.Add(entity).Entity;
_dbContext.SaveChanges();
return newEntity;
}
public override TEntity Update(TEntity entity)
{
AttachIfNot(entity);
_dbContext.Entry(entity).State = EntityState.Modified;
_dbContext.SaveChanges();
return entity;
}
public override void Delete(TEntity entity)
{
AttachIfNot(entity);
Table.Remove(entity);
_dbContext.SaveChanges();
}
public override void Delete(TPrimaryKey id)
{
var entity = GetFromChangeTrackerOrNull(id);
if (entity != null)
{
Delete(entity);
return;
}
entity = FirstOrDefault(id);
if (entity != null)
{
Delete(entity);
return;
}
}
public DbContext GetDbContext()
{
return _dbContext;
}
protected virtual void AttachIfNot(TEntity entity)
{
var entry = _dbContext.ChangeTracker.Entries().FirstOrDefault(ent => ent.Entity == entity);
if (entry != null)
{
return;
}
Table.Attach(entity);
}
private TEntity GetFromChangeTrackerOrNull(TPrimaryKey id)
{
var entry = _dbContext.ChangeTracker.Entries()
.FirstOrDefault(
ent =>
ent.Entity is TEntity &&
EqualityComparer<TPrimaryKey>.Default.Equals(id, ((TEntity)ent.Entity).Id)
);
return entry?.Entity as TEntity;
}
}
public interface IRepository<TEntity> : IRepository<TEntity, Guid>
where TEntity : class, IEntity
{
}
public interface IRepository<TEntity, TPrimaryKey>
where TEntity : class, IEntity<TPrimaryKey>
{
#region Select
/// <summary>
/// Used to get a IQueryable that is used to retrieve entities from entire table.
/// </summary>
/// <returns>IQueryable to be used to select entities from database</returns>
IQueryable<TEntity> GetAll();
/// <summary>
/// Gets an entity with given primary key.
/// </summary>
/// <param name="id">Primary key of the entity to get</param>
/// <returns>Entity</returns>
TEntity Get(TPrimaryKey id);
/// <summary>
/// Gets an entity with given primary key.
/// </summary>
/// <param name="id">Primary key of the entity to get</param>
/// <returns>Entity</returns>
Task<TEntity> GetAsync(TPrimaryKey id);
/// <summary>
/// Used to get all entities.
/// </summary>
/// <returns>List of all entities</returns>
List<TEntity> GetAllList();
/// <summary>
/// Used to get all entities.
/// </summary>
/// <returns>List of all entities</returns>
Task<List<TEntity>> GetAllListAsync();
/// <summary>
/// Used to get all entities based on given <paramref name="predicate"/>.
/// </summary>
/// <param name="predicate">A condition to filter entities</param>
/// <returns>List of all entities</returns>
List<TEntity> GetAllList(Expression<Func<TEntity, bool>> predicate);
/// <summary>
/// Used to get all entities based on given <paramref name="predicate"/>.
/// </summary>
/// <param name="predicate">A condition to filter entities</param>
/// <returns>List of all entities</returns>
Task<List<TEntity>> GetAllListAsync(Expression<Func<TEntity, bool>> predicate);
/// <summary>
/// Used to run a query over entire entities.
/// if <paramref name="queryMethod"/> finishes IQueryable with ToList, FirstOrDefault etc..
/// </summary>
/// <typeparam name="T">Type of return value of this method</typeparam>
/// <param name="queryMethod">This method is used to query over entities</param>
/// <returns>Query result</returns>
T Query<T>(Func<IQueryable<TEntity>, T> queryMethod);
/// <summary>
/// Gets an entity with given primary key or null if not found.
/// </summary>
/// <param name="id">Primary key of the entity to get</param>
/// <returns>Entity or null</returns>
TEntity FirstOrDefault(TPrimaryKey id);
/// <summary>
/// Gets an entity with given primary key or null if not found.
/// </summary>
/// <param name="id">Primary key of the entity to get</param>
/// <returns>Entity or null</returns>
Task<TEntity> FirstOrDefaultAsync(TPrimaryKey id);
/// <summary>
/// Gets an entity with given given predicate or null if not found.
/// </summary>
/// <param name="predicate">Predicate to filter entities</param>
TEntity FirstOrDefault(Expression<Func<TEntity, bool>> predicate);
/// <summary>
/// Gets an entity with given given predicate or null if not found.
/// </summary>
/// <param name="predicate">Predicate to filter entities</param>
Task<TEntity> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> predicate);
#endregion
#region Insert
/// <summary>
/// Inserts a new entity.
/// </summary>
/// <param name="entity">Inserted entity</param>
TEntity Insert(TEntity entity);
/// <summary>
/// Inserts a new entity.
/// </summary>
/// <param name="entity">Inserted entity</param>
Task<TEntity> InsertAsync(TEntity entity);
/// <summary>
/// Inserts a new entity and gets it's Id.
/// It may require to save current unit of work
/// to be able to retrieve id.
/// </summary>
/// <param name="entity">Entity</param>
/// <returns>Id of the entity</returns>
TPrimaryKey InsertAndGetId(TEntity entity);
/// <summary>
/// Inserts a new entity and gets it's Id.
/// It may require to save current unit of work
/// to be able to retrieve id.
/// </summary>
/// <param name="entity">Entity</param>
/// <returns>Id of the entity</returns>
Task<TPrimaryKey> InsertAndGetIdAsync(TEntity entity);
#endregion Insert
#region Update
/// <summary>
/// Updates an existing entity.
/// </summary>
/// <param name="entity">Entity</param>
TEntity Update(TEntity entity);
/// <summary>
/// Updates an existing entity.
/// </summary>
/// <param name="entity">Entity</param>
Task<TEntity> UpdateAsync(TEntity entity);
#endregion Update
#region Delete
/// <summary>
/// Deletes an entity.
/// </summary>
/// <param name="entity">Entity to be deleted</param>
void Delete(TEntity entity);
/// <summary>
/// Deletes an entity.
/// </summary>
/// <param name="entity">Entity to be deleted</param>
Task DeleteAsync(TEntity entity);
/// <summary>
/// Deletes an entity by primary key.
/// </summary>
/// <param name="id">Primary key of the entity</param>
void Delete(TPrimaryKey id);
/// <summary>
/// Deletes an entity by primary key.
/// </summary>
/// <param name="id">Primary key of the entity</param>
Task DeleteAsync(TPrimaryKey id);
/// <summary>
/// Deletes many entities by function.
/// Notice that: All entities fits to given predicate are retrieved and deleted.
/// This may cause major performance problems if there are too many entities with
/// given predicate.
/// </summary>
/// <param name="predicate">A condition to filter entities</param>
void Delete(Expression<Func<TEntity, bool>> predicate);
/// <summary>
/// Deletes many entities by function.
/// Notice that: All entities fits to given predicate are retrieved and deleted.
/// This may cause major performance problems if there are too many entities with
/// given predicate.
/// </summary>
/// <param name="predicate">A condition to filter entities</param>
Task DeleteAsync(Expression<Func<TEntity, bool>> predicate);
#endregion
#region Aggregates
/// <summary>
/// Gets count of all entities in this repository.
/// </summary>
/// <returns>Count of entities</returns>
int Count();
/// <summary>
/// Gets count of all entities in this repository.
/// </summary>
/// <returns>Count of entities</returns>
Task<int> CountAsync();
/// <summary>
/// Gets count of all entities in this repository based on given <paramref name="predicate"/>.
/// </summary>
/// <param name="predicate">A method to filter count</param>
/// <returns>Count of entities</returns>
int Count(Expression<Func<TEntity, bool>> predicate);
/// <summary>
/// Gets count of all entities in this repository based on given <paramref name="predicate"/>.
/// </summary>
/// <param name="predicate">A method to filter count</param>
/// <returns>Count of entities</returns>
Task<int> CountAsync(Expression<Func<TEntity, bool>> predicate);
/// <summary>
/// Gets count of all entities in this repository (use if expected return value is greather than <see cref="int.MaxValue"/>.
/// </summary>
/// <returns>Count of entities</returns>
long LongCount();
/// <summary>
/// Gets count of all entities in this repository (use if expected return value is greather than <see cref="int.MaxValue"/>.
/// </summary>
/// <returns>Count of entities</returns>
Task<long> LongCountAsync();
/// <summary>
/// Gets count of all entities in this repository based on given <paramref name="predicate"/>
/// (use this overload if expected return value is greather than <see cref="int.MaxValue"/>).
/// </summary>
/// <param name="predicate">A method to filter count</param>
/// <returns>Count of entities</returns>
long LongCount(Expression<Func<TEntity, bool>> predicate);
/// <summary>
/// Gets count of all entities in this repository based on given <paramref name="predicate"/>
/// (use this overload if expected return value is greather than <see cref="int.MaxValue"/>).
/// </summary>
/// <param name="predicate">A method to filter count</param>
/// <returns>Count of entities</returns>
Task<long> LongCountAsync(Expression<Func<TEntity, bool>> predicate);
#endregion
}
builder.RegisterType<AiOptionContext>().As<DbContext>()
.Named<DbContext>("iqoptioncontext")
.SingleInstance()
.AsImplementedInterfaces();
builder.RegisterType(typeof(EfCoreRepositoryBase<>)).As<IRepositoryWithDbContext>().InstancePerDependency();
builder.RegisterType(typeof(EfCoreRepositoryBase<,>)).As<IRepositoryWithDbContext>().InstancePerDependency();
builder.RegisterGeneric(typeof(EfCoreRepositoryBase<,>))
.As(typeof(IRepository<,>))
.WithParameters(new[]
{
new ResolvedParameter(
(p, ctx) => p.ParameterType == typeof(AiOptionContext),
(p, ctx) => ctx.ResolveNamed<DbContext>("iqoptioncontext"))
}).InstancePerDependency();
builder.RegisterGeneric(typeof(EfCoreRepositoryBase<>)).As(typeof(IRepository<>))
.WithParameters(new[]
{
new ResolvedParameter(
(p, ctx) => p.ParameterType == typeof(AiOptionContext),
(p, ctx) => ctx.ResolveNamed<DbContext>("iqoptioncontext"))
}).InstancePerDependency();
public abstract class RepositoryBase<TEntity, TPrimaryKey> : IRepository<TEntity, TPrimaryKey>
where TEntity : class, IEntity<TPrimaryKey>
{
public abstract IQueryable<TEntity> GetAll();
public virtual List<TEntity> GetAllList()
{
return GetAll().ToList();
}
public virtual Task<List<TEntity>> GetAllListAsync()
{
return Task.FromResult(GetAllList());
}
public virtual List<TEntity> GetAllList(Expression<Func<TEntity, bool>> predicate)
{
return GetAll().Where(predicate).ToList();
}
public virtual Task<List<TEntity>> GetAllListAsync(Expression<Func<TEntity, bool>> predicate)
{
return Task.FromResult(GetAllList(predicate));
}
public virtual T Query<T>(Func<IQueryable<TEntity>, T> queryMethod)
{
return queryMethod(GetAll());
}
public virtual TEntity Get(TPrimaryKey id)
{
var entity = FirstOrDefault(id);
return entity;
}
public virtual async Task<TEntity> GetAsync(TPrimaryKey id)
{
var entity = await FirstOrDefaultAsync(id);
return entity;
}
public virtual TEntity FirstOrDefault(TPrimaryKey id)
{
return GetAll().FirstOrDefault(CreateEqualityExpressionForId(id));
}
public virtual Task<TEntity> FirstOrDefaultAsync(TPrimaryKey id)
{
return Task.FromResult(FirstOrDefault(id));
}
public virtual TEntity FirstOrDefault(Expression<Func<TEntity, bool>> predicate)
{
return GetAll().FirstOrDefault(predicate);
}
public virtual Task<TEntity> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> predicate)
{
return Task.FromResult(FirstOrDefault(predicate));
}
public abstract TEntity Insert(TEntity entity);
public virtual Task<TEntity> InsertAsync(TEntity entity)
{
return Task.FromResult(Insert(entity));
}
public virtual TPrimaryKey InsertAndGetId(TEntity entity)
{
return Insert(entity).Id;
}
public virtual Task<TPrimaryKey> InsertAndGetIdAsync(TEntity entity)
{
return Task.FromResult(InsertAndGetId(entity));
}
public abstract TEntity Update(TEntity entity);
public virtual Task<TEntity> UpdateAsync(TEntity entity)
{
return Task.FromResult(Update(entity));
}
public abstract void Delete(TEntity entity);
public virtual Task DeleteAsync(TEntity entity)
{
Delete(entity);
return Task.FromResult(0);
}
public abstract void Delete(TPrimaryKey id);
public virtual Task DeleteAsync(TPrimaryKey id)
{
Delete(id);
return Task.FromResult(0);
}
public virtual void Delete(Expression<Func<TEntity, bool>> predicate)
{
foreach (var entity in GetAll().Where(predicate).ToList())
{
Delete(entity);
}
}
public virtual Task DeleteAsync(Expression<Func<TEntity, bool>> predicate)
{
Delete(predicate);
return Task.FromResult(0);
}
public virtual int Count()
{
return GetAll().Count();
}
public virtual Task<int> CountAsync()
{
return Task.FromResult(Count());
}
public virtual int Count(Expression<Func<TEntity, bool>> predicate)
{
return GetAll().Where(predicate).Count();
}
public virtual Task<int> CountAsync(Expression<Func<TEntity, bool>> predicate)
{
return Task.FromResult(Count(predicate));
}
public virtual long LongCount()
{
return GetAll().LongCount();
}
public virtual Task<long> LongCountAsync()
{
return Task.FromResult(LongCount());
}
public virtual long LongCount(Expression<Func<TEntity, bool>> predicate)
{
return GetAll().Where(predicate).LongCount();
}
public virtual Task<long> LongCountAsync(Expression<Func<TEntity, bool>> predicate)
{
return Task.FromResult(LongCount(predicate));
}
protected static Expression<Func<TEntity, bool>> CreateEqualityExpressionForId(TPrimaryKey id)
{
var lambdaParam = Expression.Parameter(typeof(TEntity));
var lambdaBody = Expression.Equal(
Expression.PropertyOrField(lambdaParam, "Id"),
Expression.Constant(id, typeof(TPrimaryKey))
);
return Expression.Lambda<Func<TEntity, bool>>(lambdaBody, lambdaParam);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment