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) 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 (!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 } }