irc-netcore-api/IRaCIS.Core.Infra.EFCore/Repository/IRaCISContextExtension.cs

250 lines
9.2 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 AutoMapper.Internal;
using EFCore.BulkExtensions;
using IRaCIS.Core.Domain.Models;
using IRaCIS.Core.Infrastructure;
using IRaCIS.Core.Infrastructure.Extention;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Query;
using SharpCompress.Factories;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Threading.Tasks;
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
}
#endregion
}
}