Commit c9b20d97 authored by 阿星Plus's avatar 阿星Plus

Plus RepositoryBase

parent 04e69807
using System;
using System.Runtime.Serialization;
namespace Plus.Domain.Entities
{
[Serializable]
public class EntityNotFoundException : PlusException
{
public Type EntityType { get; set; }
public object Id { get; set; }
public EntityNotFoundException()
{
}
public EntityNotFoundException(SerializationInfo serializationInfo, StreamingContext context)
: base(serializationInfo, context)
{
}
public EntityNotFoundException(Type entityType, object id)
: this(entityType, id, null)
{
}
public EntityNotFoundException(Type entityType, object id, Exception innerException)
: base($"There is no such an entity. Entity type: {entityType.FullName}, id: {id}", innerException)
{
EntityType = entityType;
Id = id;
}
public EntityNotFoundException(string message)
: base(message)
{
}
public EntityNotFoundException(string message, Exception innerException)
: base(message, innerException)
{
}
}
}
\ No newline at end of file
namespace Plus.Domain.Entities
{
public interface IEntity
{
}
}
\ No newline at end of file
namespace Plus.Domain.Entities
{
/// <summary>
/// Defines interface for base entity type. All entities in the system must implement this interface.
/// </summary>
/// <typeparam name="TPrimaryKey">Type of the primary key of the entity</typeparam>
public interface IEntity<TPrimaryKey>
{
/// <summary>
/// Unique identifier for this entity.
/// </summary>
TPrimaryKey Id { get; set; }
/// <summary>
/// Checks if this entity is transient (not persisted to database and it has not an <see cref="Id"/>).
/// </summary>
/// <returns>True, if this entity is transient</returns>
bool IsTransient();
}
}
\ No newline at end of file
using System;
namespace Plus.Domain.Repositories
{
/// <summary>
/// 用于为实体定义自动存储库类型,可以用于 DbContext 类型
/// </summary>
[AttributeUsage(AttributeTargets.Class)]
public class AutoRepositoryTypesAttribute : Attribute
{
public Type RepositoryInterface { get; }
public Type RepositoryInterfaceWithPrimaryKey { get; }
public Type RepositoryImplementation { get; }
public Type RepositoryImplementationWithPrimaryKey { get; }
public bool WithDefaultRepositoryInterfaces { get; set; }
public AutoRepositoryTypesAttribute(
Type repositoryInterface,
Type repositoryInterfaceWithPrimaryKey,
Type repositoryImplementation,
Type repositoryImplementationWithPrimaryKey)
{
RepositoryInterface = repositoryInterface;
RepositoryInterfaceWithPrimaryKey = repositoryInterfaceWithPrimaryKey;
RepositoryImplementation = repositoryImplementation;
RepositoryImplementationWithPrimaryKey = repositoryImplementationWithPrimaryKey;
}
}
}
\ No newline at end of file
......@@ -2,6 +2,9 @@
namespace Plus.Domain.Repositories
{
/// <summary>
/// IRepository
/// </summary>
public interface IRepository : ITransientDependency
{
......
using Plus.Domain.Entities;
namespace Plus.Domain.Repositories
{
/// <summary>
/// <see cref="IRepository{TEntity,TPrimaryKey}"/>
/// </summary>
/// <typeparam name="TEntity"></typeparam>
public interface IRepository<TEntity> : IRepository<TEntity, int> where TEntity : class, IEntity<int>
{
}
}
\ No newline at end of file
using Plus.Domain.Entities;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
namespace Plus.Domain.Repositories
{
/// <summary>
/// This interface is implemented by all repositories to ensure implementation of fixed methods.
/// </summary>
/// <typeparam name="TEntity">Main Entity type this repository works on</typeparam>
/// <typeparam name="TPrimaryKey">Primary key type of the entity</typeparam>
public interface IRepository<TEntity, TPrimaryKey> : IRepository where TEntity : class, IEntity<TPrimaryKey>
{
#region Select/Get/Query
/// <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>
/// Used to get a IQueryable that is used to retrieve entities from entire table.
/// One or more
/// </summary>
/// <param name="propertySelectors">A list of include expressions.</param>
/// <returns>IQueryable to be used to select entities from database</returns>
IQueryable<TEntity> GetAllIncluding(params Expression<Func<TEntity, object>>[] propertySelectors);
/// <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.
/// <see cref="UnitOfWorkAttribute"/> attribute is not always necessary (as opposite to <see cref="GetAll"/>)
/// 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.
/// </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>
/// Gets exactly one entity with given predicate.
/// Throws exception if no entity or more than one entity.
/// </summary>
/// <param name="predicate">Entity</param>
TEntity Single(Expression<Func<TEntity, bool>> predicate);
/// <summary>
/// Gets exactly one entity with given predicate.
/// Throws exception if no entity or more than one entity.
/// </summary>
/// <param name="predicate">Entity</param>
Task<TEntity> SingleAsync(Expression<Func<TEntity, bool>> predicate);
/// <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);
/// <summary>
/// Creates an entity with given primary key without database access.
/// </summary>
/// <param name="id">Primary key of the entity to load</param>
/// <returns>Entity</returns>
TEntity Load(TPrimaryKey id);
#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);
/// <summary>
/// Inserts or updates given entity depending on Id's value.
/// </summary>
/// <param name="entity">Entity</param>
TEntity InsertOrUpdate(TEntity entity);
/// <summary>
/// Inserts or updates given entity depending on Id's value.
/// </summary>
/// <param name="entity">Entity</param>
Task<TEntity> InsertOrUpdateAsync(TEntity entity);
/// <summary>
/// Inserts or updates given entity depending on Id's value.
/// Also returns Id of the entity.
/// 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 InsertOrUpdateAndGetId(TEntity entity);
/// <summary>
/// Inserts or updates given entity depending on Id's value.
/// Also returns Id of the entity.
/// 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> InsertOrUpdateAndGetIdAsync(TEntity entity);
#endregion
#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);
/// <summary>
/// Updates an existing entity.
/// </summary>
/// <param name="id">Id of the entity</param>
/// <param name="updateAction">Action that can be used to change values of the entity</param>
/// <returns>Updated entity</returns>
TEntity Update(TPrimaryKey id, Action<TEntity> updateAction);
/// <summary>
/// Updates an existing entity.
/// </summary>
/// <param name="id">Id of the entity</param>
/// <param name="updateAction">Action that can be used to change values of the entity</param>
/// <returns>Updated entity</returns>
Task<TEntity> UpdateAsync(TPrimaryKey id, Func<TEntity, Task> updateAction);
#endregion
#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
}
}
\ No newline at end of file
using Plus.Domain.Entities;
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;
namespace Plus.Domain.Repositories
{
public interface ISupportsExplicitLoading<TEntity, TPrimaryKey> where TEntity : class, IEntity<TPrimaryKey>
{
Task EnsureCollectionLoadedAsync<TProperty>(TEntity entity, Expression<Func<TEntity, IEnumerable<TProperty>>> collectionExpression, CancellationToken cancellationToken) where TProperty : class;
Task EnsurePropertyLoadedAsync<TProperty>(TEntity entity, Expression<Func<TEntity, TProperty>> propertyExpression, CancellationToken cancellationToken) where TProperty : class;
}
}
\ No newline at end of file
using Plus.Dependency;
using Plus.Domain.Entities;
using Plus.Domain.Uow;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
namespace Plus.Domain.Repositories
{
/// <summary>
/// <see cref="IRepository{TEntity,TPrimaryKey}"/> 实现
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <typeparam name="TPrimaryKey"></typeparam>
public abstract class PlusRepositoryBase<TEntity, TPrimaryKey> : IRepository<TEntity, TPrimaryKey>, IRepository, ITransientDependency where TEntity : class, IEntity<TPrimaryKey>
{
public IUnitOfWorkManager UnitOfWorkManager { get; set; }
public IIocResolver IocResolver { get; set; }
public abstract IQueryable<TEntity> GetAll();
public virtual IQueryable<TEntity> GetAllIncluding(params Expression<Func<TEntity, object>>[] propertySelectors)
{
return 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)
{
TEntity val = FirstOrDefault(id);
if (val == null)
{
throw new EntityNotFoundException(typeof(TEntity), id);
}
return val;
}
public virtual async Task<TEntity> GetAsync(TPrimaryKey id)
{
TEntity entity = await FirstOrDefaultAsync(id);
if (entity == null)
{
throw new EntityNotFoundException(typeof(TEntity), id);
}
return entity;
}
public virtual TEntity Single(Expression<Func<TEntity, bool>> predicate)
{
return GetAll().Single(predicate);
}
public virtual Task<TEntity> SingleAsync(Expression<Func<TEntity, bool>> predicate)
{
return Task.FromResult(Single(predicate));
}
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 virtual TEntity Load(TPrimaryKey id)
{
return Get(id);
}
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 virtual TEntity InsertOrUpdate(TEntity entity)
{
return entity.IsTransient() ? Insert(entity) : Update(entity);
}
public virtual async Task<TEntity> InsertOrUpdateAsync(TEntity entity)
{
return (!entity.IsTransient()) ? (await UpdateAsync(entity)) : (await InsertAsync(entity));
}
public virtual TPrimaryKey InsertOrUpdateAndGetId(TEntity entity)
{
return InsertOrUpdate(entity).Id;
}
public virtual Task<TPrimaryKey> InsertOrUpdateAndGetIdAsync(TEntity entity)
{
return Task.FromResult(InsertOrUpdateAndGetId(entity));
}
public abstract TEntity Update(TEntity entity);
public virtual Task<TEntity> UpdateAsync(TEntity entity)
{
return Task.FromResult(Update(entity));
}
public virtual TEntity Update(TPrimaryKey id, Action<TEntity> updateAction)
{
TEntity val = Get(id);
updateAction(val);
return val;
}
public virtual async Task<TEntity> UpdateAsync(TPrimaryKey id, Func<TEntity, Task> updateAction)
{
TEntity entity = await GetAsync(id);
await updateAction(entity);
return 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 (TEntity item in GetAll().Where(predicate).ToList())
{
Delete(item);
}
}
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 leftExpression = Expression.PropertyOrField(lambdaParam, "Id");
Expression<Func<object>> closure = () => id;
var rightExpression = Expression.Convert(closure.Body, leftExpression.Type);
var lambdaBody = Expression.Equal(leftExpression, rightExpression);
return Expression.Lambda<Func<TEntity, bool>>(lambdaBody, lambdaParam);
}
}
}
\ No newline at end of file
namespace Plus.Domain.Repositories
{
internal class UnitOfWorkExtensionDataTypes
{
public static string HardDelete { get; } = "HardDelete";
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment