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

693 lines
23 KiB
C#
Raw Permalink 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 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;
using Microsoft.Extensions.Localization;
namespace IRaCIS.Core.Infra.EFCore
{
public interface IRepository<TEntity> : ICommandRepository<TEntity>, IQueryRepository<TEntity> where TEntity : Entity
{
IRaCISDBContext _dbContext { get; set; }
}
public class Repository<TEntity> : IRepository<TEntity>
where TEntity : Entity, new()
{
public IMapper _mapper { get; set; }
public IRaCISDBContext _dbContext { get; set; }
public IStringLocalizer _localizer { get; set; }
public DbSet<TEntity> _dbSet => _dbContext.Set<TEntity>();
public IUserInfo _userInfo { get; set; }
public Repository(IRaCISDBContext dbContext, IMapper mapper, IUserInfo userInfo, IStringLocalizer localizer)
{
_localizer = localizer;
_dbContext = dbContext;
_mapper = mapper;
_userInfo = userInfo;
}
#region 异步 EF 跟踪 添加
public async Task<IEnumerable<TEntity>> AddRangeAsync(IEnumerable<TEntity> entities, bool autoSave = false)
{
await _dbSet.AddRangeAsync(entities).ConfigureAwait(false);
await SaveChangesAsync(autoSave);
return entities;
}
/// <summary>EF跟踪方式 添加</summary>
public async ValueTask<TEntity> AddAsync(TEntity entity, bool autoSave = false)
{
await _dbSet.AddAsync(entity).ConfigureAwait(false);
await SaveChangesAsync(autoSave);
return entity;
}
public async Task<TEntity> InsertFromDTOAsync<TFrom>(TFrom from, bool autoSave = false, params EntityVerifyExp<TEntity>[] verify)
{
await _dbContext.EntityVerifyAsync(true, verify);
var entity = _mapper.Map<TEntity>(from);
entity = await AddAsync(entity, autoSave);
return entity;
}
#endregion
#region 异步 EF 跟踪 部分字段更新
/// <summary>用前端传递的视图模型字段,更新,同时返回数据库该条记录的原始信息,方便对比某些字段是否更改,进行相应的逻辑操作</summary>
public async Task<TEntity> UpdateFromDTOAsync<TFrom>(TFrom from, bool autoSave = false, bool ignoreDtoNullProperty = true, params EntityVerifyExp<TEntity>[] verify)
{
var entity = _mapper.Map<TEntity>(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(_localizer["Repository_UpdateError"]);
}
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;
}
/// <summary> EF跟踪方式 生成 部分字段更新, 跟踪的实体仅有修改的属性的值有具体意义,没有从数据库查询完整的实体</summary>
public async Task UpdatePartialNoQueryAsync(Guid id, Expression<Func<TEntity, TEntity>> updateFactory, bool autoSave = false, params EntityVerifyExp<TEntity>[] verify)
{
await _dbContext.EntityVerifyAsync(false, verify, id);
var entity = new TEntity() { Id = id };
_dbContext.EntityModifyPartialFiled(entity, updateFactory);
await SaveChangesAsync(autoSave);
}
/// <summary> EF跟踪方式 生成 部分字段立即更新,默认会去处理更新更新人 更新时间 </summary>
public async Task<bool> UpdatePartialNowNoQueryAsync(Guid id, Expression<Func<TEntity, TEntity>> updateFactory,
params EntityVerifyExp<TEntity>[] verify)
{
await _dbContext.EntityVerifyAsync(false, verify, id);
var entity = new TEntity() { Id = id };
_dbContext.EntityModifyPartialFiled(entity, updateFactory);
return await SaveChangesAsync(true);
}
#endregion
#region 异步 EF 跟踪 自动生成 更新 和删除语句
/// <summary>EF跟踪方式 更新,全字段更新 不好</summary>
public async Task<bool> UpdateAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default)
{
_dbSet.Update(entity);
return await SaveChangesAsync(autoSave);
}
/// <summary>EF跟踪方式 外层先有查询好的完成实体,再更新部分字段 稽查的时候需要完整的实体信息</summary>
public async Task<bool> UpdateAsync(TEntity waitModifyEntity, Expression<Func<TEntity, TEntity>> updateFactory, bool autoSave = false, CancellationToken cancellationToken = default)
{
_dbContext.EntityModifyPartialFiled(waitModifyEntity, updateFactory);
return await SaveChangesAsync(autoSave);
}
/// <summary>EF跟踪方式 先查询出来,再更新部分字段 当在同一个事务里面需要更新同一个实体两次,请使用该方法,否则会因为重复跟踪同一个实体报错 (稽查的时候需要完整的实体信息)</summary>
public async Task<TEntity> UpdatePartialFromQueryAsync(Guid id, Expression<Func<TEntity, TEntity>> 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(_localizer["Repository_UpdateError"]);
}
_dbContext.EntityModifyPartialFiled(searchEntity, updateFactory);
await SaveChangesAsync(autoSave);
return searchEntity;
}
public async Task UpdatePartialFromQueryAsync(Expression<Func<TEntity, bool>> updateFilter,
Expression<Func<TEntity, TEntity>> 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);
}
/// <summary>EF跟踪方式 删除</summary>
public async Task<bool> DeleteAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default)
{
_dbSet.Remove(entity);
return await SaveChangesAsync(autoSave);
}
/// <summary>EF跟踪方式查询出来再删除 浪费性能,但是稽查 或者触发某些操作时,需要知道数据库实体信息 不可避免用这种)</summary>
public async Task<TEntity> 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(_localizer["Repository_DeleteError"]);
}
await DeleteAsync(waitDelete, autoSave);
return waitDelete;
}
/// <summary>批量删除EF跟踪方式所有查询出来再删除 浪费性能,但是稽查 或者触发某些操作时,需要知道数据库实体信息 不可避免用这种)</summary>
public async Task<List<TEntity>> DeleteFromQueryAsync(Expression<Func<TEntity, bool>> 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<List<TEntity>> SoftDeleteFromQueryAsync(Expression<Func<TEntity, bool>> 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 跟踪机制的删除 更新 以及批量操作
/// <summary>批量删除相当于原生sql 没用EF跟踪方式所有查询出来再删除 浪费性能)</summary>
public async Task<bool> BatchDeleteNoTrackingAsync(Expression<Func<TEntity, bool>> deleteFilter)
{
return await _dbContext.BatchDeleteNoTrackingAsync(deleteFilter);
}
/// <summary>批量更新相当于原生sql 没用EF跟踪方式所有查询出来再更新 浪费性能)</summary>
public async Task<bool> BatchUpdateNoTrackingAsync(Expression<Func<TEntity, bool>> where,
Expression<Func<TEntity, TEntity>> updateFactory)
{
return await _dbContext.BatchUpdateNoTrackingAsync(where, updateFactory,_userInfo.Id);
}
#endregion
#region 保存 、忽略 、验证
public async Task<TEntity> InsertOrUpdateAsync<TFrom>(TFrom from, bool autoSave = false, params EntityVerifyExp<TEntity>[] verify)
{
var entity = _mapper.Map<TEntity>(from);
if (entity.Id == Guid.Empty)
{
return await InsertFromDTOAsync(from, autoSave, verify);
}
else
{
return await UpdateFromDTOAsync(from, autoSave, false, verify);
}
}
private async Task<bool> SaveChangesAsync(bool autoSave)
{
if (autoSave)
{
return await SaveChangesAsync();
}
else
{
return false;
}
}
/// <summary>
/// 忽略空值属性
/// </summary>
/// <param name="entity"></param>
/// <param name="ignoreNullValues"></param>
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<bool> SaveChangesAsync(CancellationToken cancellationToken = default)
{
return await _dbContext.SaveChangesAsync(cancellationToken) > 0;
}
#endregion
#region 不常用
/// <summary>EF跟踪方式 生成 部分字段更新 (只更新传递的字段名 new[] {nameof(User.Name), nameof(User.Age))</summary>
public async Task<TEntity> UpdatePartialFieldsAsync(TEntity entity, string[] propertyNames,
bool autoSave = false, bool ignoreEntityNullProperty = true, params EntityVerifyExp<TEntity>[] 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;
}
/// <summary>更新 排除某些字段的更新 排除方式: new[] {nameof(User.Name), nameof(User.Age)</summary>
public async Task<TEntity> UpdateExcludeFields(TEntity entity, string[] propertyNames, bool autoSave = false, bool ignoreEntityNullProperty = true, params EntityVerifyExp<TEntity>[] 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<TResult> MaxAsync<TResult>(Expression<Func<TEntity, TResult>> selector)
{
return await _dbSet.AsNoTracking().MaxAsync(selector);
}
public async Task<bool> AnyAsync(Expression<Func<TEntity, bool>> exp, bool ignoreQueryFilters = false)
{
var query = _dbSet.AsQueryable();
if (ignoreQueryFilters)
{
query = query.IgnoreQueryFilters();
}
return await query.AsNoTracking().AnyAsync(exp);
}
public bool Any(Expression<Func<TEntity, bool>> exp, bool ignoreQueryFilters = false)
{
var query = _dbSet.AsQueryable();
if (ignoreQueryFilters)
{
query = query.IgnoreQueryFilters();
}
return query.AsNoTracking().Any(exp);
}
public async Task<int> CountAsync(Expression<Func<TEntity, bool>> 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<TEntity> FindAsync(Guid id, CancellationToken cancellationToken = default)
{
return await _dbContext.FindAsync<TEntity>(id);
}
//有可能是联合主键本项目没用到用Guid
public async ValueTask<TEntity> FindAsync(object[] keyValues, CancellationToken cancellationToken)
{
return await _dbContext.FindAsync<TEntity>(keyValues);
}
public async Task<TEntity> FirstAsync(Expression<Func<TEntity, bool>> 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<TEntity> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> 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);
}
/// <summary>
/// 不跟踪
/// </summary>
/// <param name="exp"></param>
/// <param name="ignoreQueryFilters"></param>
/// <returns></returns>
public async Task<TEntity> FirstOrDefaultNoTrackingAsync(Expression<Func<TEntity, bool>> 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<TEntity> AsQueryable(bool ignoreQueryFilters = false)
{
var query = _dbSet.AsQueryable();
if (ignoreQueryFilters)
{
query = query.IgnoreQueryFilters();
}
return query.AsNoTracking();
}
public IQueryable<TResult> Select<TResult>(Expression<Func<TEntity, TResult>> selector)
{
return _dbSet.AsNoTracking().Select(selector);
}
public IQueryable<TEntity> WhereIf(bool condition, Expression<Func<TEntity, bool>> filter)
{
return condition ? _dbSet.AsNoTracking().Where(filter) : _dbSet.AsNoTracking();
}
public IQueryable<TEntity> Where(Expression<Func<TEntity, bool>> exp = null, bool isTraking = false, bool ignoreQueryFilters = false)
{
IQueryable<TEntity> 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<TEntity> Attach(TEntity entity)
{
return _dbSet.Attach(entity);
}
public void Detached(TEntity t)
{
_dbContext.Entry(t).State = EntityState.Detached;
}
// automapper 相关
public IQueryable<TDestination> ProjectTo<TDestination>(IConfigurationProvider configuration, object parameters, params Expression<Func<TDestination, object>>[] membersToExpand)
{
return _dbSet.AsNoTracking().ProjectTo(configuration, parameters, membersToExpand);
}
public IQueryable<TDestination> ProjectTo<TDestination>(IConfigurationProvider configuration, params Expression<Func<TDestination, object>>[] membersToExpand)
{
return _dbSet.AsNoTracking().ProjectTo(configuration, membersToExpand);
}
public IQueryable<TDestination> ProjectTo<TDestination>(IConfigurationProvider configuration, IDictionary<string, object> parameters, params string[] membersToExpand)
{
return _dbSet.AsNoTracking().ProjectTo<TDestination>(configuration, parameters, membersToExpand);
}
#endregion
}
}