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
}
}