249 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			C#
		
	
	
			
		
		
	
	
			249 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			C#
		
	
	
| using IRaCIS.Core.Domain.Models;
 | ||
| using IRaCIS.Core.Infrastructure;
 | ||
| using IRaCIS.Core.Infrastructure.Extention;
 | ||
| using Microsoft.EntityFrameworkCore.Query;
 | ||
| using System.Reflection;
 | ||
| 
 | ||
| namespace IRaCIS.Core.Infra.EFCore
 | ||
| {
 | ||
|     public class EntityVerifyExp<TEntity> where TEntity : Entity
 | ||
|     {
 | ||
|         //验证表达式树
 | ||
|         public Expression<Func<TEntity, bool>> VerifyExp { get; set; }
 | ||
| 
 | ||
|         //验证提示错误信息
 | ||
|         public string VerifyMsg { get; set; }
 | ||
| 
 | ||
|         public VerifyEnum verifyType { get; set; } = VerifyEnum.Both;
 | ||
| 
 | ||
|         public bool IsVerify { get; set; } = true;
 | ||
|     }
 | ||
| 
 | ||
|     public enum VerifyEnum
 | ||
|     {
 | ||
|         OnlyAdd = 1,
 | ||
| 
 | ||
|         OnlyUpdate = 2,
 | ||
| 
 | ||
|         Both = 3,
 | ||
|     }
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
|     public static class EntityAction
 | ||
|     {
 | ||
|         /// <summary>
 | ||
|         ///添加和更新的时候,通常需要与数据库已存在的数据进行校验,添加更新的区分在于是否需要排除自己
 | ||
|         /// </summary>
 | ||
|         /// <typeparam name="T"></typeparam>
 | ||
|         /// <param name="_dbContext"></param>
 | ||
|         /// <param name="isAdd"></param>
 | ||
|         /// <param name="verify"></param>
 | ||
|         /// <param name="entitydId"></param>
 | ||
|         /// <returns></returns>
 | ||
|         /// <exception cref="BusinessValidationFailedException"></exception>
 | ||
|         public static async Task EntityVerifyAsync<T>(this IRaCISDBContext _dbContext, 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>().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>().AnyAsync(verifyItem.VerifyExp).ConfigureAwait(false))
 | ||
|                         {
 | ||
|                             throw new BusinessValidationFailedException(verifyItem.VerifyMsg);
 | ||
|                         }
 | ||
|                     }
 | ||
|                     else if (verifyItem.verifyType == VerifyEnum.Both)
 | ||
|                     {
 | ||
|                         if (await _dbContext.Set<T>().AnyAsync(verifyItem.VerifyExp.And(t => t.Id != entitydId)).ConfigureAwait(false))
 | ||
|                         {
 | ||
|                             throw new BusinessValidationFailedException(verifyItem.VerifyMsg);
 | ||
|                         }
 | ||
|                     }
 | ||
|                 }
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
|         public static async Task EntityVerifyAsync<T>(this IRepository<T> _entityRepository, Guid? entitydId = null, params EntityVerifyExp<T>[] verify) where T : Entity
 | ||
|         {
 | ||
| 
 | ||
|             if (entitydId == null)
 | ||
|             {
 | ||
|                 foreach (var verifyItem in verify.Where(t => t.verifyType != VerifyEnum.OnlyUpdate && t.IsVerify))
 | ||
|                 {
 | ||
|                     if (await _entityRepository.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 _entityRepository.AnyAsync(verifyItem.VerifyExp).ConfigureAwait(false))
 | ||
|                         {
 | ||
|                             throw new BusinessValidationFailedException(verifyItem.VerifyMsg);
 | ||
|                         }
 | ||
|                     }
 | ||
|                     else if (verifyItem.verifyType == VerifyEnum.Both)
 | ||
|                     {
 | ||
|                         if (await _entityRepository.AnyAsync(verifyItem.VerifyExp.And(t => t.Id != entitydId)).ConfigureAwait(false))
 | ||
|                         {
 | ||
|                             throw new BusinessValidationFailedException(verifyItem.VerifyMsg);
 | ||
|                         }
 | ||
|                     }
 | ||
|                 }
 | ||
|             }
 | ||
|         }
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
|         ///注意 模型标注了 ConcurrencyCheck的属性,这样的实体,不适合用部分字段更新,ef生成的更新sql会自动带上ConcurrencyCheck的属性条件
 | ||
|         /// <summary>EntityState.Detached的实体   修改 部分字段</summary>
 | ||
|         public static void EntityModifyPartialFiled<T>(this IRaCISDBContext _dbContext, T waitModifyEntity, Expression<Func<T, T>> updateFactory) where T : Entity
 | ||
|         {
 | ||
|             var entityEntry = _dbContext.Entry(waitModifyEntity);
 | ||
|             //entityEntry.State = EntityState.Detached;
 | ||
| 
 | ||
|             var 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)
 | ||
|             {
 | ||
|                 _dbContext.Entry(waitModifyEntity).Property(prop.Name).IsModified = true;
 | ||
| 
 | ||
|                 object value = prop.GetValue(applyObj);
 | ||
|                 prop.SetValue(waitModifyEntity, value);
 | ||
| 
 | ||
|             }
 | ||
| 
 | ||
| 
 | ||
|         }
 | ||
| 
 | ||
| 
 | ||
|         #region 不走EF 跟踪机制的删除 更新  以及批量操作
 | ||
| 
 | ||
|         /// <summary>批量删除,相当于原生sql,  没用EF跟踪方式(所有查询出来,再删除 浪费性能)</summary>
 | ||
|         public static async Task<bool> BatchDeleteNoTrackingAsync<T>(this IRaCISDBContext _dbContext, Expression<Func<T, bool>> deleteFilter) where T : Entity
 | ||
|         {
 | ||
|             if (deleteFilter == null) throw new ArgumentNullException(nameof(deleteFilter));
 | ||
| 
 | ||
|             return await _dbContext.Set<T>().IgnoreQueryFilters().Where(deleteFilter).ExecuteDeleteAsync() > 0;
 | ||
|         }
 | ||
| 
 | ||
| 
 | ||
|         /// <summary>批量更新,相当于原生sql,  没用EF跟踪方式(所有查询出来,再更新 浪费性能)</summary>
 | ||
|         public static async Task<bool> BatchUpdateNoTrackingAsync<T>(this IRaCISDBContext _dbContext, Expression<Func<T, bool>> where, Expression<Func<T, T>> updateFactory, Guid updateUserId, bool isAutoIncludeTimeAndUser = true) where T : Entity
 | ||
|         {
 | ||
|             if (where == null) throw new ArgumentNullException(nameof(where));
 | ||
| 
 | ||
| 
 | ||
|             #region history 使用扩展删除包,同时自动赋值更新人  更新时间
 | ||
| 
 | ||
|             //var bindings = ((MemberInitExpression)updateFactory.Body).Bindings.ToList();
 | ||
| 
 | ||
|             //var hasPropNameList = bindings.Select(t => t.Member.Name).ToList();
 | ||
| 
 | ||
| 
 | ||
|             //if (typeof(IAuditUpdate).IsAssignableFrom(typeof(T)))
 | ||
|             //{
 | ||
| 
 | ||
|             //    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(updateUserId)));
 | ||
| 
 | ||
|             //    }
 | ||
|             //}
 | ||
| 
 | ||
|             //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>().IgnoreQueryFilters().Where(where).BatchUpdateAsync(factory).ConfigureAwait(false) > 0;
 | ||
| 
 | ||
|             #endregion
 | ||
| 
 | ||
| 
 | ||
|             #region efcore 7 & 8
 | ||
|             {
 | ||
|                 var fieldValues = updateFactory.ExtractFieldValues();
 | ||
| 
 | ||
|                 var hasPropNameList = ((MemberInitExpression)updateFactory.Body).Bindings.Select(t => t.Member.Name).ToList();
 | ||
| 
 | ||
|                 if (typeof(IAuditUpdate).IsAssignableFrom(typeof(T)))
 | ||
|                 {
 | ||
|                     if (isAutoIncludeTimeAndUser)
 | ||
|                     {
 | ||
|                         if (!hasPropNameList.Contains(nameof(IAuditUpdate.UpdateTime)))
 | ||
|                         {
 | ||
|                             fieldValues.Add(nameof(IAuditUpdate.UpdateTime), DateTime.Now);
 | ||
|                         }
 | ||
| 
 | ||
|                         if (!hasPropNameList.Contains(nameof(IAuditUpdate.UpdateUserId)))
 | ||
|                         {
 | ||
|                             fieldValues.Add(nameof(IAuditUpdate.UpdateUserId), updateUserId);
 | ||
|                         }
 | ||
|                     }
 | ||
|                 }
 | ||
| 
 | ||
|                 return await _dbContext.Set<T>().IgnoreQueryFilters().Where(where).ExecuteUpdateAsync(fieldValues).ConfigureAwait(false) > 0;
 | ||
|             }
 | ||
| 
 | ||
| 
 | ||
|             #endregion
 | ||
| 
 | ||
|         }
 | ||
| 
 | ||
| 
 | ||
|         public static async Task<bool> ExecuteUpdateAsync<T>(this IRaCISDBContext _dbContext, Expression<Func<T, bool>> where, Expression<Func<SetPropertyCalls<T>, SetPropertyCalls<T>>> setPropertyCalls) where T : Entity
 | ||
|         {
 | ||
| 
 | ||
|             return await _dbContext.Set<T>().Where(where).ExecuteUpdateAsync<T>(setPropertyCalls) > 0;
 | ||
| 
 | ||
|         }
 | ||
| 
 | ||
| 
 | ||
|         #endregion
 | ||
|     }
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| }
 | ||
| 
 | ||
| 
 | ||
| 
 | ||
| 
 |