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

731 lines
26 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 System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
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.Data.SqlClient;
using Z.EntityFramework.Plus;
namespace IRaCIS.Core.Infra.EFCore
{
public class Repository<TEntity> : IRepository<TEntity>
where TEntity : Entity, new()
{
public IMapper _mapper { get; set; }
public IRaCISDBContext _dbContext { get; set; }
public DbSet<TEntity> _dbSet => _dbContext.Set<TEntity>();
public IUserInfo _userInfo { get; set; }
public Repository(IRaCISDBContext dbContext, IMapper mapper,IUserInfo userInfo)
{
_dbContext = dbContext;
_mapper = mapper;
_userInfo = userInfo;
}
#region 异步部分
/// <summary>
/// 仅仅供字典表插入使用因为efcore 动态映射列的问题
/// </summary>
/// <typeparam name="TFrom"></typeparam>
/// <param name="from"></param>
/// <param name="verify"></param>
/// <returns></returns>
/// <exception cref="BusinessValidationFailedException"></exception>
public async Task<TEntity> InsertDictionaryAsync<TFrom>(TFrom from, params EntityVerifyExp<TEntity>[] verify)
{
var entity = _mapper.Map<TEntity>(from);
foreach (var verifyItem in verify.Where(t => t.verifyType != VerifyEnum.OnlyUpdate && t.IsVerify))
{
if (await _dbSet.IgnoreQueryFilters().AnyAsync(verifyItem.VerifyExp).ConfigureAwait(false))
{
throw new BusinessValidationFailedException(verifyItem.VerifyMsg);
}
}
if (typeof(TEntity) == typeof(Dictionary))
{
Type type = typeof(TFrom);
//以下是不要ID这个字段的 比如自增列ID 就不能像上名那样写
var properties = type.GetProperties().Where(t => t.Name != "Id");
string strSqlName = string.Join(",", properties.Select(p => $"[{p.Name}]").ToArray());
string strSqlValue = string.Join(",", properties.Select(P => $"@{P.Name}").ToArray());
string strSql = $"insert into {nameof(Dictionary)} ( " + strSqlName + " ) values (" + strSqlValue + ")";
//para Sql是参数
SqlParameter[] para = properties.Select(p => new SqlParameter($"@{p.Name}", p.GetValue(from, null))).ToArray();
_dbContext.Database.ExecuteSqlRaw(strSql, para);
return entity;
}
else
{
throw new Exception("仅仅供字典表插入使用因为efcore 动态映射列的问题");
//await _dbSet.BulkInsertAsync(new List<TEntity>() { entity });
//return entity;
}
}
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 EntityVerifyAsync(bool isAdd, EntityVerifyExp<TEntity>[] verify, Guid? entitydId = null)
{
if (isAdd)
{
foreach (var verifyItem in verify.Where(t => t.verifyType != VerifyEnum.OnlyUpdate && t.IsVerify))
{
if (await _dbSet.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 _dbSet.IgnoreQueryFilters().AnyAsync(verifyItem.VerifyExp).ConfigureAwait(false))
{
throw new BusinessValidationFailedException(verifyItem.VerifyMsg);
}
}
else if (verifyItem.verifyType == VerifyEnum.Both)
{
if (await _dbSet.IgnoreQueryFilters().AnyAsync(verifyItem.VerifyExp.And(t => t.Id != entitydId)).ConfigureAwait(false))
{
throw new BusinessValidationFailedException(verifyItem.VerifyMsg);
}
}
}
}
}
public async Task<TEntity> InsertFromDTOAsync<TFrom>(TFrom from, bool autoSave = false, params EntityVerifyExp<TEntity>[] verify)
{
var entity = _mapper.Map<TEntity>(from);
await EntityVerifyAsync(true, verify);
await _dbSet.AddAsync(entity).ConfigureAwait(false);
await SaveChangesAsync(autoSave);
return entity;
}
/// <summary>
/// 用前端传递的视图模型字段,更新,同时返回数据库该条记录的原始信息,方便对比某些字段是否更改,进行相应的逻辑操作
/// </summary>
/// <typeparam name="TFrom"></typeparam>
/// <param name="from"></param>
/// <param name="autoSave"></param>
/// <param name="ignoreDtoNullProperty"></param>
/// <param name="verify"></param>
/// <returns></returns>
/// <exception cref="BusinessValidationFailedException"></exception>
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);
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;
}
private async Task<bool> SaveChangesAsync(bool autoSave)
{
if (autoSave)
{
return await SaveChangesAsync();
}
else
{
return false;
}
}
/// <summary>
/// 通过主键id 和表达式树 更新部分字段 例如 Guid.Parse("8a90c96e-0776-4f7b-82a6-18933d339584"),u => new Dictionary() { ParentId = null, Code = "test" } 默认会去处理更新更新人 更新时间
/// </summary>
/// <param name="id"></param>
/// <param name="updateFactory"></param>
/// <param name="autoSave"></param>
/// <param name="verify"></param>
/// <returns></returns>
public async Task<TEntity> UpdatePartialFields(Guid id, Expression<Func<TEntity, TEntity>> updateFactory, bool autoSave = false, params EntityVerifyExp<TEntity>[] verify)
{
await EntityVerifyAsync(false, verify, id);
var entity = new TEntity() { Id = id };
var entityEntry = _dbContext.Entry(entity);
entityEntry.State = EntityState.Detached;
List<PropertyInfo> list = ((MemberInitExpression)updateFactory.Body).Bindings.Select(mb => mb.Member.Name)
.Select(propName => typeof(TEntity).GetProperty(propName, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)).ToList();
Func<TEntity, TEntity> func = updateFactory.Compile();
TEntity applyObj = func(entity);
foreach (PropertyInfo prop in list)
{
object value = prop.GetValue((object)applyObj);
prop.SetValue((object)entity, value);
_dbContext.Entry(entity).Property(prop.Name).IsModified = true;
}
await SaveChangesAsync(autoSave);
return entityEntry.Entity;
#region Test
//updateFactory.Compile()(entity);
//List<string> propNameList = ((MemberInitExpression)updateFactory.Body).Bindings.Select(mb => mb.Member.Name).ToList();
//foreach (string propName in propNameList)
//{
// _dbContext.Entry(entity).Property(propName).IsModified = true;
//}
#endregion
}
/// <summary>
/// 部分字段更新 (只更新传递的字段名 new[] {nameof(User.Name), nameof(User.Age))
/// new Dictionary() { ParentId = null, Code = "test"Id=Guid.Parse("8a90c96e-0776-4f7b-82a6-18933d339584")},new[] {nameof(Dictionary.Name), nameof(Dictionary.Age))
/// </summary>
/// <param name="entity"></param>
/// <param name="propertyNames"> 更新的字段数组 </param>
/// <param name="autoSave"></param>
/// <param name="ignoreEntityNullProperty"></param>
/// <param name="verify"></param>
/// <returns></returns>
public async Task<TEntity> UpdatePartialFields(TEntity entity, string[] propertyNames,
bool autoSave = false, bool ignoreEntityNullProperty = true, params EntityVerifyExp<TEntity>[] verify)
{
await 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>
/// <param name="entity"></param>
/// <param name="propertyNames"></param>
/// <param name="autoSave"></param>
/// <param name="ignoreEntityNullProperty"></param>
/// <param name="verify"></param>
/// <returns></returns>
public async Task<TEntity> UpdateExcludeFields(TEntity entity, string[] propertyNames, bool autoSave = false, bool ignoreEntityNullProperty = true, params EntityVerifyExp<TEntity>[] verify)
{
await 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;
}
/// <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;
}
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> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> exp = null, bool ignoreQueryFilters = false)
{
var query = _dbSet.AsQueryable();
if (ignoreQueryFilters)
{
query = query.IgnoreQueryFilters();
}
if (exp == null)
return await query.FirstOrDefaultAsync().ConfigureAwait(false);
return await query.FirstOrDefaultAsync(exp).ConfigureAwait(false);
}
public async ValueTask<TEntity> AddAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default)
{
await _dbSet.AddAsync(entity).ConfigureAwait(false);
if (autoSave)
{
await SaveChangesAsync(cancellationToken);
}
return entity;
}
public async Task<IEnumerable<TEntity>> AddRangeAsync(IEnumerable<TEntity> entities)
{
await _dbSet.AddRangeAsync(entities).ConfigureAwait(false);
return entities;
}
public async Task<bool> AddRangeAsync(IEnumerable<TEntity> entities, bool autoSave = false, CancellationToken cancellationToken = default)
{
await _dbSet.AddRangeAsync(entities).ConfigureAwait(false);
if (autoSave)
{
return await SaveChangesAsync(cancellationToken);
}
else
{
return false;
}
}
public async Task<bool> UpdateAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default)
{
_dbSet.Update(entity);
if (autoSave)
{
return await SaveChangesAsync(cancellationToken);
}
else
{
return false;
}
}
public async Task<bool> UpdateManyAsync(IEnumerable<TEntity> entities, bool autoSave = false, CancellationToken cancellationToken = default)
{
_dbSet.UpdateRange(entities);
if (autoSave)
{
return await SaveChangesAsync(cancellationToken);
}
else
{
return false;
}
}
public async Task<bool> DeleteAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default)
{
_dbSet.Remove(entity);
//var entry = _dbSet.Attach(entity);
//entry.State = EntityState.Deleted;
if (autoSave)
{
return await SaveChangesAsync(cancellationToken);
}
else
{
return false;
}
}
public async Task<bool> DeleteManyAsync(IEnumerable<TEntity> entities, bool autoSave = false, CancellationToken cancellationToken = default)
{
_dbSet.RemoveRange(entities);
if (autoSave)
{
return await SaveChangesAsync(cancellationToken);
}
else
{
return false;
}
}
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 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 Task<bool> BatchDeleteAsync(Expression<Func<TEntity, bool>> deleteFilter)
{
return await _dbSet.IgnoreQueryFilters().Where(deleteFilter).BatchDeleteAsync() > 0;
}
public async Task<bool> BatchUpdateAsync(Expression<Func<TEntity, bool>> where,
Expression<Func<TEntity, TEntity>> updateFactory)
{
//return await _dbSet.IgnoreQueryFilters().Where(where).BatchUpdateAsync(updateFactory) > 0;
var bindings = ((MemberInitExpression)updateFactory.Body).Bindings.ToList();
if (typeof(IAuditUpdate).IsAssignableFrom(typeof(TEntity)))
{
bindings.Add(Expression.Bind(typeof(TEntity).GetMember(nameof(IAuditUpdate.UpdateTime))[0], Expression.Constant(DateTime.Now)));
bindings.Add(Expression.Bind(typeof(TEntity).GetMember(nameof(IAuditUpdate.UpdateUserId))[0], Expression.Constant(_userInfo.Id)));
}
var member = Expression.MemberInit(Expression.New(typeof(TEntity)), bindings);
var factory = Expression.Lambda<Func<TEntity, TEntity>>(member, Expression.Parameter(typeof(TEntity), "x"));
return await _dbSet.IgnoreQueryFilters().Where(where).BatchUpdateAsync(factory) > 0;
}
/// <summary>
/// 杨中科 test 有问题 对于guid 赋值null的时候
/// </summary>
/// <param name="where"></param>
/// <param name="updateFactory"></param>
/// <returns></returns>
//public async Task<bool> BatchUpdateAsync(Expression<Func<TEntity, bool>> where,
// Expression<Func<TEntity, TEntity>> updateFactory)
//{
// var builder = _dbContext.BatchUpdate<TEntity>();
// Func<TEntity, TEntity> func = updateFactory.Compile();
// List<PropertyInfo> list = ((MemberInitExpression)updateFactory.Body).Bindings.Select<MemberBinding, string>((Func<MemberBinding, string>)(_param1 => _param1.Member.Name)).Select<string, PropertyInfo>((Func<string, PropertyInfo>)(_param1 => (PropertyInfo)typeof(TEntity).GetProperty(_param1, BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic))).ToList<PropertyInfo>();
// TEntity applyObj = func(new TEntity());
// foreach (PropertyInfo prop in list)
// {
// var propName = prop.Name;
// var value = prop.GetValue(applyObj);
// builder.Set(propName, value?? DBNull.Value);
// //PropertyInfo property = typeof(TEntity).GetProperty(prop.Name);
// //Type propertyType = property.PropertyType;
// //ParameterExpression parameterExpression = Expression.Parameter(typeof(TEntity));
// //Type delegateType = typeof(Func<,>).MakeGenericType(typeof(TEntity), propertyType);
// //LambdaExpression nameExpr = Expression.Lambda(delegateType, (Expression)Expression.MakeMemberAccess((Expression)parameterExpression, (MemberInfo)property), parameterExpression);
// //Expression expression = (Expression)Expression.Constant(value);
// //expression = (Expression)Expression.Convert(expression, propertyType);
// //LambdaExpression valueExpr = Expression.Lambda(delegateType, expression, parameterExpression);
// //builder.Set< >(Func<TEntity, c> nameExpr, valueExpr);
// }
// return await builder.Where(where).ExecuteAsync()>0;
//}
//// Z.EntityFramework.Plus.EFCore
//public async Task<bool> BatchDeleteAsync(Expression<Func<TEntity, bool>> deleteFilter)
//{
// return await _dbSet.IgnoreQueryFilters().Where(deleteFilter).DeleteFromQueryAsync() > 0;
//}
//public async Task<bool> BatchUpdateAsync(Expression<Func<TEntity, bool>> where, Expression<Func<TEntity, TEntity>> updateFactory)
//{
// return await _dbSet.IgnoreQueryFilters().Where(where).UpdateFromQueryAsync(updateFactory) > 0;
//}
#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
}
}