using System; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; using System.Threading; using System.Threading.Tasks; using AutoMapper; using IRaCIS.Core.Domain.Models; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.ChangeTracking; using AutoMapper.QueryableExtensions; using EFCore.BulkExtensions; using IRaCIS.Core.Domain.Share; using IRaCIS.Core.Infrastructure; using IRaCIS.Core.Infrastructure.Extention; namespace IRaCIS.Core.Infra.EFCore { public interface IRepository : ICommandRepository, IQueryRepository where TEntity : Entity { IRaCISDBContext _dbContext { get; set; } } public class Repository : IRepository where TEntity : Entity, new() { public IMapper _mapper { get; set; } public IRaCISDBContext _dbContext { get; set; } public DbSet _dbSet => _dbContext.Set(); public IUserInfo _userInfo { get; set; } public Repository(IRaCISDBContext dbContext, IMapper mapper, IUserInfo userInfo) { _dbContext = dbContext; _mapper = mapper; _userInfo = userInfo; } #region 异步 EF 跟踪 添加 public async Task> AddRangeAsync(IEnumerable entities, bool autoSave = false) { await _dbSet.AddRangeAsync(entities).ConfigureAwait(false); await SaveChangesAsync(autoSave); return entities; } /// EF跟踪方式 添加 public async ValueTask AddAsync(TEntity entity, bool autoSave = false) { await _dbSet.AddAsync(entity).ConfigureAwait(false); await SaveChangesAsync(autoSave); return entity; } public async Task InsertFromDTOAsync(TFrom from, bool autoSave = false, params EntityVerifyExp[] verify) { await _dbContext.EntityVerifyAsync(true, verify); var entity = _mapper.Map(from); entity = await AddAsync(entity, autoSave); return entity; } #endregion #region 异步 EF 跟踪 部分字段更新 /// 用前端传递的视图模型字段,更新,同时返回数据库该条记录的原始信息,方便对比某些字段是否更改,进行相应的逻辑操作 public async Task UpdateFromDTOAsync(TFrom from, bool autoSave = false, bool ignoreDtoNullProperty = true, params EntityVerifyExp[] verify) { var entity = _mapper.Map(from); //await EntityVerifyAsync(false, verify, entity.Id); await _dbContext.EntityVerifyAsync(false, verify, entity.Id); var dbEntity = await _dbSet.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(TEntity).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; } /// EF跟踪方式 生成 部分字段更新, 跟踪的实体仅有修改的属性的值有具体意义,没有从数据库查询完整的实体 public async Task UpdatePartialNoQueryAsync(Guid id, Expression> updateFactory, bool autoSave = false, params EntityVerifyExp[] verify) { await _dbContext.EntityVerifyAsync(false, verify, id); var entity = new TEntity() { Id = id }; _dbContext.EntityModifyPartialFiled(entity, updateFactory); await SaveChangesAsync(autoSave); } /// EF跟踪方式 生成 部分字段立即更新,默认会去处理更新更新人 更新时间 public async Task UpdatePartialNowNoQueryAsync(Guid id, Expression> updateFactory, params EntityVerifyExp[] verify) { await _dbContext.EntityVerifyAsync(false, verify, id); var entity = new TEntity() { Id = id }; _dbContext.EntityModifyPartialFiled(entity, updateFactory); return await SaveChangesAsync(true); } #endregion #region 异步 EF 跟踪 自动生成 更新 和删除语句 /// EF跟踪方式 更新,全字段更新 不好 public async Task UpdateAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default) { _dbSet.Update(entity); return await SaveChangesAsync(autoSave); } /// EF跟踪方式 外层先有查询好的完成实体,再更新部分字段 稽查的时候需要完整的实体信息 public async Task UpdateAsync(TEntity waitModifyEntity, Expression> updateFactory, bool autoSave = false, CancellationToken cancellationToken = default) { _dbContext.EntityModifyPartialFiled(waitModifyEntity, updateFactory); return await SaveChangesAsync(autoSave); } /// EF跟踪方式 先查询出来,再更新部分字段 当在同一个事务里面需要更新同一个实体两次,请使用该方法,否则会因为重复跟踪同一个实体报错 (稽查的时候需要完整的实体信息) public async Task UpdatePartialFromQueryAsync(Guid id, Expression> updateFactory, bool autoSave = false, CancellationToken cancellationToken = default) { //var query = ignoreQueryFilter ? _dbSet.AsNoTracking().IgnoreQueryFilters() : _dbSet.AsNoTracking(); //不跟踪 查询出来的实体就是Detached var searchEntity = await _dbSet.FindAsync( id); if (searchEntity == null) { throw new BusinessValidationFailedException( " Update object not exist in db,Please check if the parameter Id is passed incorrectly"); } _dbContext.EntityModifyPartialFiled(searchEntity, updateFactory); await SaveChangesAsync(autoSave); return searchEntity; } public async Task UpdatePartialFromQueryAsync(Expression> updateFilter, Expression> updateFactory, bool autoSave = false, bool ignoreQueryFilter = false, CancellationToken cancellationToken = default) { if (updateFilter == null) { throw new ArgumentException("更新过滤条件不允许为空", nameof(updateFilter)); } var query = ignoreQueryFilter ? _dbSet.AsNoTracking().IgnoreQueryFilters() : _dbSet.AsNoTracking(); var searchEntityList = await query.Where(updateFilter).ToListAsync(); foreach (var needUpdateEntity in searchEntityList) { await UpdateAsync(needUpdateEntity, updateFactory, false); } await SaveChangesAsync(autoSave); } /// EF跟踪方式 删除 public async Task DeleteAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default) { _dbSet.Remove(entity); return await SaveChangesAsync(autoSave); } /// EF跟踪方式(查询出来,再删除 浪费性能,但是稽查 或者触发某些操作时,需要知道数据库实体信息 不可避免用这种) public async Task DeleteFromQueryAsync(Guid id, bool autoSave = false, bool ignoreQueryFilter = false) { var query = ignoreQueryFilter ? _dbSet.AsNoTracking().IgnoreQueryFilters() : _dbSet.AsNoTracking(); var waitDelete = await query.Where(t => t.Id == id).FirstOrDefaultAsync(); if (waitDelete == null) { throw new BusinessValidationFailedException( " Delete object not exist in db,Please check if the parameter Id is passed incorrectly"); } await DeleteAsync(waitDelete, autoSave); return waitDelete; } /// 批量删除,EF跟踪方式(所有查询出来,再删除 浪费性能,但是稽查 或者触发某些操作时,需要知道数据库实体信息 不可避免用这种) public async Task> DeleteFromQueryAsync(Expression> deleteFilter, bool autoSave = false, bool ignoreQueryFilter = false) { var query = ignoreQueryFilter ? _dbSet.AsNoTracking().IgnoreQueryFilters() : _dbSet.AsNoTracking(); var waitDeleteList = await query.Where(deleteFilter).ToListAsync(); _dbSet.RemoveRange(waitDeleteList); await SaveChangesAsync(autoSave); return waitDeleteList; } public async Task> SoftDeleteFromQueryAsync(Expression> deleteFilter, bool autoSave = false, bool ignoreQueryFilter = false) { var query = ignoreQueryFilter ? _dbSet.IgnoreQueryFilters() : _dbSet; var waitDeleteList = await query.Where(deleteFilter).ToListAsync(); foreach (var deleteItem in waitDeleteList) { if (deleteItem is ISoftDelete softDeleteItem) { softDeleteItem.IsDeleted = true; } } await SaveChangesAsync(autoSave); return waitDeleteList; } #endregion #region 不走EF 跟踪机制的删除 更新 以及批量操作 /// 批量删除,相当于原生sql, 没用EF跟踪方式(所有查询出来,再删除 浪费性能) public async Task BatchDeleteNoTrackingAsync(Expression> deleteFilter) { return await _dbContext.BatchDeleteNoTrackingAsync(deleteFilter); } /// 批量更新,相当于原生sql, 没用EF跟踪方式(所有查询出来,再更新 浪费性能) public async Task BatchUpdateNoTrackingAsync(Expression> where, Expression> updateFactory) { return await _dbContext.BatchUpdateNoTrackingAsync(where, updateFactory,_userInfo.Id); } #endregion #region 保存 、忽略 、验证 public async Task InsertOrUpdateAsync(TFrom from, bool autoSave = false, params EntityVerifyExp[] verify) { var entity = _mapper.Map(from); if (entity.Id == Guid.Empty) { return await InsertFromDTOAsync(from, autoSave, verify); } else { return await UpdateFromDTOAsync(from, autoSave, false, verify); } } private async Task SaveChangesAsync(bool autoSave) { if (autoSave) { return await SaveChangesAsync(); } else { return false; } } /// /// 忽略空值属性 /// /// /// private void IgnoreNullValues(ref TEntity entity, bool? ignoreNullValues = null) { var isIgnore = ignoreNullValues; if (isIgnore == false) return; // 获取所有的属性 var properties = _dbSet.EntityType.GetProperties(); if (properties == null) return; foreach (var propety in properties) { var entityProperty = _dbContext.Entry(entity).Property(propety.Name); var propertyValue = entityProperty?.CurrentValue; var propertyType = entityProperty?.Metadata?.PropertyInfo?.PropertyType; // 判断是否是无效的值,比如为 null,默认时间,以及空 Guid 值 var isInvalid = propertyValue == null || (propertyType == typeof(DateTime) && propertyValue?.ToString() == new DateTime().ToString()) || (propertyType == typeof(DateTimeOffset) && propertyValue?.ToString() == new DateTimeOffset().ToString()) || (propertyType == typeof(Guid) && propertyValue?.ToString() == Guid.Empty.ToString()); if (isInvalid && entityProperty != null) { entityProperty.IsModified = false; } } } public async Task SaveChangesAsync(CancellationToken cancellationToken = default) { return await _dbContext.SaveChangesAsync(cancellationToken) > 0; } #endregion #region 不常用 /// EF跟踪方式 生成 部分字段更新 (只更新传递的字段名 new[] {nameof(User.Name), nameof(User.Age)) public async Task UpdatePartialFieldsAsync(TEntity entity, string[] propertyNames, bool autoSave = false, bool ignoreEntityNullProperty = true, params EntityVerifyExp[] verify) { await _dbContext.EntityVerifyAsync(false, verify, entity.Id); var entityEntry = _dbContext.Entry(entity); entityEntry.State = EntityState.Detached; foreach (var propertyName in propertyNames) { _dbContext.Entry(entity).Property(propertyName).IsModified = true; } // 忽略空值 IgnoreNullValues(ref entity, ignoreEntityNullProperty); return entityEntry.Entity; } /// 更新 排除某些字段的更新 排除方式: new[] {nameof(User.Name), nameof(User.Age) public async Task UpdateExcludeFields(TEntity entity, string[] propertyNames, bool autoSave = false, bool ignoreEntityNullProperty = true, params EntityVerifyExp[] verify) { await _dbContext.EntityVerifyAsync(false, verify, entity.Id); var entityEntry = _dbContext.Entry(entity); entityEntry.State = EntityState.Modified; foreach (var propertyName in propertyNames) { _dbContext.Entry(entity).Property(propertyName).IsModified = false; } // 忽略空值 IgnoreNullValues(ref entity, ignoreEntityNullProperty); return entityEntry.Entity; } #endregion #region 异步查询 public async Task MaxAsync(Expression> selector) { return await _dbSet.AsNoTracking().MaxAsync(selector); } public async Task AnyAsync(Expression> exp, bool ignoreQueryFilters = false) { var query = _dbSet.AsQueryable(); if (ignoreQueryFilters) { query = query.IgnoreQueryFilters(); } return await query.AsNoTracking().AnyAsync(exp); } public bool Any(Expression> exp, bool ignoreQueryFilters = false) { var query = _dbSet.AsQueryable(); if (ignoreQueryFilters) { query = query.IgnoreQueryFilters(); } return query.AsNoTracking().Any(exp); } public async Task CountAsync(Expression> whereLambda = null, bool ignoreQueryFilters = false) { var query = _dbSet.AsQueryable(); if (ignoreQueryFilters) { query = query.IgnoreQueryFilters(); } return whereLambda == null ? await query.AsNoTracking().CountAsync() : await query.AsNoTracking().CountAsync(whereLambda); } public async ValueTask FindAsync(Guid id, CancellationToken cancellationToken = default) { return await _dbContext.FindAsync(id); } //有可能是联合主键,本项目没用到,用Guid public async ValueTask FindAsync(object[] keyValues, CancellationToken cancellationToken) { return await _dbContext.FindAsync(keyValues); } public async Task FirstAsync(Expression> exp = null, bool isTracking = false, bool ignoreQueryFilters = false) { var query = _dbSet.AsQueryable(); if (!isTracking) { query = query.AsNoTracking(); } if (ignoreQueryFilters) { query = query.IgnoreQueryFilters(); } var entity = await query.FirstOrDefaultAsync(exp); if (entity is null) { throw new QueryBusinessObjectNotExistException($"The query object {typeof(TEntity).Name} does not exist in database, Please check the query parameters"); } else { return entity; } } public async Task FirstOrDefaultAsync(Expression> exp = null, bool ignoreQueryFilters = false) { var query = _dbSet.AsQueryable(); if (ignoreQueryFilters) { query = query.IgnoreQueryFilters(); } if (exp != null) { query = query.Where(exp); } return await query.FirstOrDefaultAsync().ConfigureAwait(false); } /// /// 不跟踪 /// /// /// /// public async Task FirstOrDefaultNoTrackingAsync(Expression> exp = null, bool ignoreQueryFilters = false) { var query = _dbSet.AsNoTracking().AsQueryable(); if (ignoreQueryFilters) { query = query.IgnoreQueryFilters(); } if (exp != null) { query = query.Where(exp); } return await query.AsNoTracking().FirstOrDefaultAsync().ConfigureAwait(false); } #endregion #region 非异步部分 public TEntity ImageFind(Guid id, Type type) { //重传的时候 状态为修改 上传的时候状态为添加 内存中有,就不要重复查询数据库了 var list = _dbContext.ChangeTracker.Entries() .Where(u => (u.State == EntityState.Added || u.State == EntityState.Modified) && (u.Entity.GetType() == type)).Select(t => t.Entity as TEntity); var entity = list.FirstOrDefault(t => t.Id == id); if (entity == null) { return _dbSet.FirstOrDefault(t => t.Id == id); } else { return entity; } } public IQueryable AsQueryable(bool ignoreQueryFilters = false) { var query = _dbSet.AsQueryable(); if (ignoreQueryFilters) { query = query.IgnoreQueryFilters(); } return query.AsNoTracking(); } public IQueryable Select(Expression> selector) { return _dbSet.AsNoTracking().Select(selector); } public IQueryable WhereIf(bool condition, Expression> filter) { return condition ? _dbSet.AsNoTracking().Where(filter) : _dbSet.AsNoTracking(); } public IQueryable Where(Expression> exp = null, bool isTraking = false, bool ignoreQueryFilters = false) { IQueryable query = _dbSet; if (!isTraking) { query = query.AsNoTracking(); } if (ignoreQueryFilters) { query = query.IgnoreQueryFilters(); } if (exp != null) { query = query.Where(exp); } return query; } public EntityEntry Entry(TEntity t) { return _dbContext.Entry(t); } public EntityEntry Attach(TEntity entity) { return _dbSet.Attach(entity); } public void Detached(TEntity t) { _dbContext.Entry(t).State = EntityState.Detached; } // automapper 相关 public IQueryable ProjectTo(IConfigurationProvider configuration, object parameters, params Expression>[] membersToExpand) { return _dbSet.AsNoTracking().ProjectTo(configuration, parameters, membersToExpand); } public IQueryable ProjectTo(IConfigurationProvider configuration, params Expression>[] membersToExpand) { return _dbSet.AsNoTracking().ProjectTo(configuration, membersToExpand); } public IQueryable ProjectTo(IConfigurationProvider configuration, IDictionary parameters, params string[] membersToExpand) { return _dbSet.AsNoTracking().ProjectTo(configuration, parameters, membersToExpand); } #endregion } }