EI-Image-Viewer-Api/IRaCIS.Core.Infra.EFCore/Repository/IRepository.cs

565 lines
18 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using AutoMapper;
using IRaCIS.Core.Domain.Models;
using IRaCIS.Core.Infrastructure;
using IRaCIS.Core.Infrastructure.Extention;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using EFCore.BulkExtensions;
using IRaCIS.Core.Domain.Share;
using IRaCIS.Core.Infra.EFCore.Common.Dto;
using System.Reflection;
namespace IRaCIS.Core.Infra.EFCore
{
public interface IRepository<TEntity> : ICommandRepository<TEntity>, IQueryRepository<TEntity> where TEntity : Entity
{
IRaCISDBContext _dbContext { get; set; }
/// <summary>
/// 获取中心啥的名称
/// </summary>
/// <param name="Data"></param>
/// <returns></returns>
Task SetInspectionNameValue(DataInspection Data);
/// <summary>
/// 格式化日期和时间
/// </summary>
/// <param name="Data"></param>
/// <returns></returns>
Task<DataInspection> SetDataInspectionDateType(DataInspection Data);
/// <summary>
/// 翻译稽查数据
/// </summary>
/// <param name="dto">传入Dto</param>
/// <returns></returns>
Task<List<string>> SetInspectionEnumValue(SetInspectionEnumValueDto dto);
}
#region 泛型通用版本
public interface IRepository
{
IQueryable<T> GetQueryable<T>(bool isTraking = false) where T : Entity;
DbSet<T> Set<T>() where T : Entity;
IQueryable<T> WhereIf<T>(bool condition, Expression<Func<T, bool>> filter) where T : Entity;
Task<T> InsertOrUpdateAsync<T, TFrom>(TFrom from, bool autoSave = false, params EntityVerifyExp<T>[] verify) where T : Entity;
Task<T> InsertFromDTOAsync<T, TFrom>(TFrom from, bool autoSave = false, params EntityVerifyExp<T>[] verify) where T : Entity;
Task<T> UpdateFromDTOAsync<T, TFrom>(TFrom from, bool autoSave = false, bool ignoreDtoNullProperty = true, params EntityVerifyExp<T>[] verify) where T : Entity;
EntityEntry<T> Entry<T>(T t) where T : Entity;
Task<bool> AnyAsync<T>(Expression<Func<T, bool>> filter, bool ignoreQueryFilters = false) where T : Entity;
Task<T> FirstOrDefaultAsync<T>(Expression<Func<T, bool>> exp = null, bool ignoreQueryFilters = false) where T : Entity;
Task<int> CountAsync<T>(Expression<Func<T, bool>> exp = null, bool ignoreQueryFilters = false) where T : Entity;
IQueryable<T> Where<T>(Expression<Func<T, bool>> exp = null, bool isTraking = false, bool ignoreQueryFilters = false) where T : Entity;
ValueTask<T> FindAsync<T>(Guid id) where T : Entity;
ValueTask<T> AddAsync<T>(T entity, bool autoSave = false) where T : Entity;
Task UpdateRange<T>(IEnumerable<T> entities, bool autoSave = false) where T : Entity;
Task<bool> AddRangeAsync<T>(IEnumerable<T> entities, bool autoSave = false) where T : Entity;
Task<bool> UpdateAsync<T>(T entity, bool autoSave = false) where T : Entity;
Task<bool> DeleteAsync<T>(T entity, bool autoSave = false) where T : Entity;
//Task<bool> DeleteManyAsync<T>(IEnumerable<T> entities, bool autoSave = false) where T : Entity;
Task<bool> SaveChangesAsync();
Task<bool> BatchDeleteAsync<T>(Expression<Func<T, bool>> deleteFilter) where T : Entity;
Task<bool> BatchUpdateAsync<T>(Expression<Func<T, bool>> where, Expression<Func<T, T>> updateFactory) where T : Entity;
Task UpdatePartialFromQueryAsync<T>(Expression<Func<T, bool>> updateFilter,
Expression<Func<T, T>> updateFactory,
bool autoSave = false, bool ignoreQueryFilter = false) where T : Entity;
}
public class Repository : IRepository
{
private IRaCISDBContext _dbContext { get; }
public IMapper _mapper { get; set; }
public IUserInfo _userInfo { get; set; }
public Repository(IRaCISDBContext dbContext, IMapper mapper, IUserInfo userInfo)
{
_dbContext = dbContext;
_mapper = mapper;
_userInfo = userInfo;
}
/// <summary>
/// 设置是使用哪个仓储 默认不跟踪
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="isTraking"></param>
/// <returns></returns>
public IQueryable<T> GetQueryable<T>(bool isTraking = false) where T : Entity
{
IQueryable<T> query = _dbContext.Set<T>();
if (!isTraking)
{
query = query.AsNoTracking();
}
return query;
}
public DbSet<T> Set<T>() where T : Entity
{
return _dbContext.Set<T>();
}
public IQueryable<T> WhereIf<T>(bool condition, Expression<Func<T, bool>> filter) where T : Entity
{
IQueryable<T> query = _dbContext.Set<T>().AsNoTracking();
return condition ? query.Where(filter) : query;
}
private async Task EntityVerifyAsync<T>(bool isAdd, EntityVerifyExp<T>[] verify, Guid? entitydId = null) where T : Entity
{
if (isAdd)
{
foreach (var verifyItem in verify.Where(t => t.verifyType != VerifyEnum.OnlyUpdate && t.IsVerify))
{
if (await _dbContext.Set<T>().IgnoreQueryFilters().AnyAsync(verifyItem.VerifyExp).ConfigureAwait(false))
{
throw new BusinessValidationFailedException(verifyItem.VerifyMsg);
}
}
}
else
{
foreach (var verifyItem in verify.Where(t => t.verifyType != VerifyEnum.OnlyAdd && t.IsVerify))
{
if (verifyItem.verifyType == VerifyEnum.OnlyUpdate)
{
if (await _dbContext.Set<T>().IgnoreQueryFilters().AnyAsync(verifyItem.VerifyExp).ConfigureAwait(false))
{
throw new BusinessValidationFailedException(verifyItem.VerifyMsg);
}
}
else if (verifyItem.verifyType == VerifyEnum.Both)
{
if (await _dbContext.Set<T>().IgnoreQueryFilters().AnyAsync(verifyItem.VerifyExp.And(t => t.Id != entitydId)).ConfigureAwait(false))
{
throw new BusinessValidationFailedException(verifyItem.VerifyMsg);
}
}
}
}
}
public async Task<T> InsertFromDTOAsync<T, TFrom>(TFrom from, bool autoSave = false,
params EntityVerifyExp<T>[] verify) where T : Entity
{
var entity = _mapper.Map<T>(from);
await EntityVerifyAsync(true, verify);
entity = await AddAsync(entity, autoSave);
return entity;
}
public async Task<T> UpdateFromDTOAsync<T, TFrom>(TFrom from, bool autoSave = false, bool ignoreDtoNullProperty = true, params EntityVerifyExp<T>[] verify) where T : Entity
{
var entity = _mapper.Map<T>(from);
await EntityVerifyAsync(false, verify, entity.Id);
var dbEntity = await _dbContext.Set<T>().IgnoreQueryFilters().FirstOrDefaultAsync(t => t.Id == entity.Id).ConfigureAwait(false);
if (dbEntity == null)
{
throw new BusinessValidationFailedException(
" Update object not exist in db,Please check if the parameter Id is passed incorrectly");
}
var dbBeforEntity = dbEntity.Clone();
_mapper.Map(from, dbEntity);
//DTO null 属性不更新 防止意外操作,导致保存数据错误,或者 add 和update 用一个模型更新的时候只传递了部分字段导致不想更新的字段因为没传递值用null覆盖了
// Guid属性 为null 时 映射到 Guid 时 默认会变成 Guid.Empty
if (ignoreDtoNullProperty)
{
var dbEntityProp = typeof(T).GetProperties();
foreach (var propertyInfo in from.GetType().GetProperties())
{
if (propertyInfo.GetValue(from) == null && dbEntityProp.Any(t => t.Name == propertyInfo.Name))
{
_dbContext.Entry(dbEntity).Property(propertyInfo.Name).IsModified = false;
}
}
}
await SaveChangesAsync(autoSave);
return dbBeforEntity;
}
private async Task<bool> SaveChangesAsync(bool autoSave)
{
if (autoSave)
{
return await SaveChangesAsync();
}
else
{
return false;
}
}
public async Task<T> InsertOrUpdateAsync<T, TFrom>(TFrom from, bool autoSave = false, params EntityVerifyExp<T>[] verify) where T : Entity
{
var entity = _mapper.Map<T>(from);
if (entity.Id == Guid.Empty)
{
return await InsertFromDTOAsync(from, autoSave, verify);
}
else
{
return await UpdateFromDTOAsync(from, autoSave, false, verify);
}
}
public EntityEntry<T> Entry<T>(T t) where T : Entity
{
return _dbContext.Entry<T>(t);
}
public async Task<bool> AnyAsync<T>(Expression<Func<T, bool>> filter, bool ignoreQueryFilters = false) where T : Entity
{
var query = _dbContext.Set<T>().AsQueryable();
if (ignoreQueryFilters)
{
query = query.IgnoreQueryFilters();
}
return await query.AsNoTracking().AnyAsync(filter).ConfigureAwait(false);
}
#region 基本查询 异步
/// <summary>
/// 跟踪查询某个实体 --异步 默认是跟踪查询
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="exp"></param>
/// <returns></returns>
///
public async Task<int> CountAsync<T>(Expression<Func<T, bool>> exp = null, bool ignoreQueryFilters = false) where T : Entity
{
var query = _dbContext.Set<T>().AsQueryable();
if (ignoreQueryFilters)
{
query = query.IgnoreQueryFilters();
}
if (exp != null)
{
query = query.Where(exp);
}
return await query.CountAsync().ConfigureAwait(false);
}
public async Task<T> FirstOrDefaultAsync<T>(Expression<Func<T, bool>> exp = null, bool ignoreQueryFilters = false) where T : Entity
{
var query = _dbContext.Set<T>().AsQueryable();
if (ignoreQueryFilters)
{
query = query.IgnoreQueryFilters();
}
if (exp != null)
{
query = query.Where(exp);
}
return await query.FirstOrDefaultAsync().ConfigureAwait(false);
}
/// <summary>
/// 过滤 默认不跟踪
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="exp"></param>
/// <param name="isTraking"></param>
/// <returns></returns>
public IQueryable<T> Where<T>(Expression<Func<T, bool>> exp = null, bool isTraking = false, bool ignoreQueryFilters = false) where T : Entity
{
IQueryable<T> query = _dbContext.Set<T>();
if (ignoreQueryFilters)
{
query = query.IgnoreQueryFilters();
}
if (!isTraking)
{
query = query.AsNoTracking();
}
if (exp != null)
{
query = query.Where(exp);
}
return query;
}
/// <summary>
/// 首先在内存中查找,其次再找数据库
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="id"></param>
/// <returns></returns>
public async ValueTask<T> FindAsync<T>(Guid id) where T : Entity
{
return await _dbContext.Set<T>().FindAsync(id).ConfigureAwait(false);
}
#endregion
#region 基本添加、更新、删除 异步
public async ValueTask<T> AddAsync<T>(T entity, bool autoSave = false) where T : Entity
{
await _dbContext.Set<T>().AddAsync(entity).ConfigureAwait(false);
if (autoSave)
{
await SaveChangesAsync();
}
return entity;
}
public async Task<bool> AddRangeAsync<T>(IEnumerable<T> entities, bool autoSave = false) where T : Entity
{
await _dbContext.Set<T>().AddRangeAsync(entities).ConfigureAwait(false);
if (autoSave)
{
return await SaveChangesAsync();
}
else
{
return false;
}
}
public async Task UpdateRange<T>(IEnumerable<T> entities, bool autoSave = false) where T : Entity
{
_dbContext.Set<T>().UpdateRange(entities);
if (autoSave)
{
await SaveChangesAsync();
}
}
public async Task<bool> UpdateAsync<T>(T entity, bool autoSave = false) where T : Entity
{
_dbContext.Set<T>().Update(entity);
if (autoSave)
{
return await SaveChangesAsync();
}
else
{
return false;
}
}
public async Task<bool> DeleteAsync<T>(T entity, bool autoSave = false) where T : Entity
{
_dbContext.Set<T>().Remove(entity);
if (autoSave)
{
return await SaveChangesAsync();
}
else
{
return false;
}
}
public async Task<bool> DeleteManyAsync<T>(IEnumerable<T> entities, bool autoSave = false) where T : Entity
{
_dbContext.Set<T>().RemoveRange(entities);
if (autoSave)
{
return await SaveChangesAsync();
}
else
{
return false;
}
}
public async Task<bool> SaveChangesAsync()
{
return await _dbContext.SaveChangesAsync().ConfigureAwait(false) > 0;
}
#endregion
public async Task<bool> BatchDeleteAsync<T>(Expression<Func<T, bool>> deleteFilter) where T : Entity
{
if (deleteFilter == null) throw new ArgumentNullException(nameof(deleteFilter));
return await _dbContext.Set<T>().AsNoTracking().IgnoreQueryFilters().Where(deleteFilter).BatchDeleteAsync().ConfigureAwait(false) > 0;
}
public async Task<bool> BatchUpdateAsync<T>(Expression<Func<T, bool>> whereFilter, Expression<Func<T, T>> updateFactory) where T : Entity
{
if (whereFilter == null) throw new ArgumentNullException(nameof(whereFilter));
var bindings = ((MemberInitExpression)updateFactory.Body).Bindings.ToList();
if (typeof(IAuditUpdate).IsAssignableFrom(typeof(T)))
{
var hasPropNameList = bindings.Select(t => t.Member.Name).ToList();
if (!hasPropNameList.Contains(nameof(IAuditUpdate.UpdateTime)))
{
bindings.Add(Expression.Bind(typeof(T).GetMember(nameof(IAuditUpdate.UpdateTime))[0], Expression.Constant(DateTime.Now)));
}
if (!hasPropNameList.Contains(nameof(IAuditUpdate.UpdateUserId)))
{
bindings.Add(Expression.Bind(typeof(T).GetMember(nameof(IAuditUpdate.UpdateUserId))[0], Expression.Constant(_userInfo.Id)));
}
}
var member = Expression.MemberInit(Expression.New(typeof(T)), bindings);
var factory = Expression.Lambda<Func<T, T>>(member, Expression.Parameter(typeof(T), "x"));
return await _dbContext.Set<T>().AsNoTracking().IgnoreQueryFilters().Where(whereFilter).BatchUpdateAsync(updateFactory).ConfigureAwait(false) > 0;
}
public async Task UpdatePartialFromQueryAsync<T>(Expression<Func<T, bool>> updateFilter,
Expression<Func<T, T>> updateFactory,
bool autoSave = false, bool ignoreQueryFilter = false) where T : Entity
{
if (updateFilter == null)
{
throw new ArgumentException("更新过滤条件不允许为空", nameof(updateFilter));
}
var query = ignoreQueryFilter ? _dbContext.Set<T>().AsNoTracking().IgnoreQueryFilters() : _dbContext.Set<T>().AsNoTracking();
var searchEntityList = await query.Where(updateFilter).ToListAsync();
foreach (var needUpdateEntity in searchEntityList)
{
await UpdateAsync(needUpdateEntity, updateFactory, autoSave);
}
}
public async Task<bool> UpdateAsync<T>(T waitModifyEntity, Expression<Func<T, T>> updateFactory, bool autoSave = false) where T : Entity
{
var entityEntry = _dbContext.Entry(waitModifyEntity);
entityEntry.State = EntityState.Detached;
ModifyPartialFiled(waitModifyEntity, updateFactory);
return await SaveChangesAsync(autoSave);
}
/// <summary>更新后拥有完整的实体信息,便于稽查</summary>
private void ModifyPartialFiled<T>(T waitModifyEntity, Expression<Func<T, T>> updateFactory)
{
List<PropertyInfo> list = ((MemberInitExpression)updateFactory.Body).Bindings.Select(mb => mb.Member.Name)
.Select(propName => typeof(T).GetProperty(propName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)).ToList();
Func<T, T> func = updateFactory.Compile();
T applyObj = func(waitModifyEntity);
//深拷贝更新之前实体信息
//var copyObj = waitModifyEntity.Clone();
foreach (PropertyInfo prop in list)
{
object value = prop.GetValue(applyObj);
prop.SetValue(waitModifyEntity, value);
_dbContext.Entry(waitModifyEntity).Property(prop.Name).IsModified = true;
}
}
}
#endregion
}