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 System.Reflection; namespace IRaCIS.Core.Infra.EFCore { #region 泛型通用版本 public interface IRepository { IQueryable GetQueryable(bool isTraking = false) where T : Entity; DbSet Set() where T : Entity; EntityEntry Entry(T t) where T : Entity; Task SaveChangesAsync(); IQueryable WhereIf(bool condition, Expression> filter) where T : Entity; Task AnyAsync(Expression> filter, bool ignoreQueryFilters = false) where T : Entity; Task FirstOrDefaultAsync(Expression> exp = null, bool ignoreQueryFilters = false) where T : Entity; Task CountAsync(Expression> exp = null, bool ignoreQueryFilters = false) where T : Entity; IQueryable Where(Expression> exp = null, bool isTraking = false, bool ignoreQueryFilters = false) where T : Entity; ValueTask FindAsync(Guid id) where T : Entity; Task InsertOrUpdateAsync(TFrom from, bool autoSave = false, params EntityVerifyExp[] verify) where T : Entity; Task InsertFromDTOAsync(TFrom from, bool autoSave = false, params EntityVerifyExp[] verify) where T : Entity; Task UpdateFromDTOAsync(TFrom from, bool autoSave = false, bool ignoreDtoNullProperty = true, params EntityVerifyExp[] verify) where T : Entity; ValueTask AddAsync(T entity, bool autoSave = false) where T : Entity; Task UpdateRange(IEnumerable entities, bool autoSave = false) where T : Entity; Task AddRangeAsync(IEnumerable entities, bool autoSave = false) where T : Entity; Task UpdateAsync(T entity, bool autoSave = false) where T : Entity; Task DeleteAsync(T entity, bool autoSave = false) where T : Entity; Task BatchDeleteAsync(Expression> deleteFilter) where T : Entity; Task BatchUpdateAsync(Expression> where, Expression> updateFactory) where T : Entity; Task UpdatePartialFromQueryAsync(Expression> updateFilter, Expression> updateFactory, bool autoSave = false, bool ignoreQueryFilter = false) where T : Entity; } public class Repository : IRepository { #region 构造 基本 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; } /// /// 设置是使用哪个仓储 默认不跟踪 /// /// /// /// public IQueryable GetQueryable(bool isTraking = false) where T : Entity { IQueryable query = _dbContext.Set(); if (!isTraking) { query = query.AsNoTracking(); } return query; } public DbSet Set() where T : Entity { return _dbContext.Set(); } public EntityEntry Entry(T t) where T : Entity { return _dbContext.Entry(t); } public IQueryable WhereIf(bool condition, Expression> filter) where T : Entity { IQueryable query = _dbContext.Set().AsNoTracking(); return condition ? query.Where(filter) : query; } private async Task SaveChangesAsync(bool autoSave) { if (autoSave) { return await SaveChangesAsync(); } else { return false; } } public async Task SaveChangesAsync() { return await _dbContext.SaveChangesAsync().ConfigureAwait(false) > 0; } #endregion private async Task EntityVerifyAsync(bool isAdd, EntityVerifyExp[] 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().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().AnyAsync(verifyItem.VerifyExp).ConfigureAwait(false)) { throw new BusinessValidationFailedException(verifyItem.VerifyMsg); } } else if (verifyItem.verifyType == VerifyEnum.Both) { if (await _dbContext.Set().AnyAsync(verifyItem.VerifyExp.And(t => t.Id != entitydId)).ConfigureAwait(false)) { throw new BusinessValidationFailedException(verifyItem.VerifyMsg); } } } } } public async Task InsertFromDTOAsync(TFrom from, bool autoSave = false, params EntityVerifyExp[] verify) where T : Entity { var entity = _mapper.Map(from); await EntityVerifyAsync(true, verify); entity = await AddAsync(entity, autoSave); return entity; } public async Task UpdateFromDTOAsync(TFrom from, bool autoSave = false, bool ignoreDtoNullProperty = true, params EntityVerifyExp[] verify) where T : Entity { var entity = _mapper.Map(from); await EntityVerifyAsync(false, verify, entity.Id); var dbEntity = await _dbContext.Set().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; } public async Task InsertOrUpdateAsync(TFrom from, bool autoSave = false, params EntityVerifyExp[] verify) where T : Entity { var entity = _mapper.Map(from); if (entity.Id == Guid.Empty) { return await InsertFromDTOAsync(from, autoSave, verify); } else { return await UpdateFromDTOAsync(from, autoSave, false, verify); } } public async Task AnyAsync(Expression> filter, bool ignoreQueryFilters = false) where T : Entity { var query = _dbContext.Set().AsQueryable(); if (ignoreQueryFilters) { query = query.IgnoreQueryFilters(); } return await query.AsNoTracking().AnyAsync(filter).ConfigureAwait(false); } #region 基本查询 异步 /// /// 跟踪查询某个实体 --异步 默认是跟踪查询 /// /// /// /// /// public async Task CountAsync(Expression> exp = null, bool ignoreQueryFilters = false) where T : Entity { var query = _dbContext.Set().AsQueryable(); if (ignoreQueryFilters) { query = query.IgnoreQueryFilters(); } if (exp != null) { query = query.Where(exp); } return await query.CountAsync().ConfigureAwait(false); } public async Task FirstOrDefaultAsync(Expression> exp = null, bool ignoreQueryFilters = false) where T : Entity { var query = _dbContext.Set().AsQueryable(); if (ignoreQueryFilters) { query = query.IgnoreQueryFilters(); } if (exp != null) { query = query.Where(exp); } return await query.FirstOrDefaultAsync().ConfigureAwait(false); } /// /// 过滤 默认不跟踪 /// /// /// /// /// public IQueryable Where(Expression> exp = null, bool isTraking = false, bool ignoreQueryFilters = false) where T : Entity { IQueryable query = _dbContext.Set(); if (ignoreQueryFilters) { query = query.IgnoreQueryFilters(); } if (!isTraking) { query = query.AsNoTracking(); } if (exp != null) { query = query.Where(exp); } return query; } /// /// 首先在内存中查找,其次再找数据库 /// /// /// /// public async ValueTask FindAsync(Guid id) where T : Entity { return await _dbContext.Set().FindAsync(id).ConfigureAwait(false); } #endregion #region 基本添加、更新、删除 异步 public async ValueTask AddAsync(T entity, bool autoSave = false) where T : Entity { await _dbContext.Set().AddAsync(entity).ConfigureAwait(false); if (autoSave) { await SaveChangesAsync(); } return entity; } public async Task AddRangeAsync(IEnumerable entities, bool autoSave = false) where T : Entity { await _dbContext.Set().AddRangeAsync(entities).ConfigureAwait(false); return await SaveChangesAsync(autoSave); } public async Task UpdateRange(IEnumerable entities, bool autoSave = false) where T : Entity { _dbContext.Set().UpdateRange(entities); await SaveChangesAsync(autoSave); } public async Task UpdateAsync(T entity, bool autoSave = false) where T : Entity { _dbContext.Set().Update(entity); return await SaveChangesAsync(autoSave); } public async Task DeleteAsync(T entity, bool autoSave = false) where T : Entity { _dbContext.Set().Remove(entity); return await SaveChangesAsync(autoSave); } public async Task DeleteManyAsync(IEnumerable entities, bool autoSave = false) where T : Entity { _dbContext.Set().RemoveRange(entities); return await SaveChangesAsync(autoSave); } #endregion public async Task BatchDeleteAsync(Expression> deleteFilter) where T : Entity { if (deleteFilter == null) throw new ArgumentNullException(nameof(deleteFilter)); return await _dbContext.Set().AsNoTracking().IgnoreQueryFilters().Where(deleteFilter).BatchDeleteAsync().ConfigureAwait(false) > 0; } public async Task BatchUpdateAsync(Expression> whereFilter, Expression> 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>(member, Expression.Parameter(typeof(T), "x")); return await _dbContext.Set().AsNoTracking().IgnoreQueryFilters().Where(whereFilter).BatchUpdateAsync(updateFactory).ConfigureAwait(false) > 0; } public async Task UpdatePartialFromQueryAsync(Expression> updateFilter, Expression> updateFactory, bool autoSave = false, bool ignoreQueryFilter = false) where T : Entity { if (updateFilter == null) { throw new ArgumentException("更新过滤条件不允许为空", nameof(updateFilter)); } var query = ignoreQueryFilter ? _dbContext.Set().AsNoTracking().IgnoreQueryFilters() : _dbContext.Set().AsNoTracking(); var searchEntityList = await query.Where(updateFilter).ToListAsync(); foreach (var needUpdateEntity in searchEntityList) { await UpdateAsync(needUpdateEntity, updateFactory, autoSave); } } public async Task UpdateAsync(T waitModifyEntity, Expression> updateFactory, bool autoSave = false) where T : Entity { var entityEntry = _dbContext.Entry(waitModifyEntity); entityEntry.State = EntityState.Detached; ModifyPartialFiled(waitModifyEntity, updateFactory); return await SaveChangesAsync(autoSave); } /// 更新后拥有完整的实体信息,便于稽查 private void ModifyPartialFiled(T waitModifyEntity, Expression> updateFactory) { List list = ((MemberInitExpression)updateFactory.Body).Bindings.Select(mb => mb.Member.Name) .Select(propName => typeof(T).GetProperty(propName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)).ToList(); Func 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 }