937 lines
33 KiB
C#
937 lines
33 KiB
C#
using System;
|
||
using System.Collections.Generic;
|
||
using System.ComponentModel.DataAnnotations;
|
||
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 Newtonsoft.Json;
|
||
using IRaCIS.Core.Infra.EFCore.Dto;
|
||
|
||
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 异步部分
|
||
|
||
|
||
|
||
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)
|
||
{
|
||
List<DataInspection> datas = new List<DataInspection>();
|
||
var entity = _mapper.Map<TEntity>(from);
|
||
|
||
await EntityVerifyAsync(true, verify);
|
||
|
||
await _dbSet.AddAsync(entity).ConfigureAwait(false);
|
||
|
||
await SaveChangesAsync(autoSave);
|
||
|
||
|
||
|
||
var createtime = DateTime.Now.AddSeconds(1);
|
||
|
||
// 受试者
|
||
if (typeof(TEntity) == typeof(Subject))
|
||
{
|
||
Subject data = entity as Subject;
|
||
datas.Add(new DataInspection()
|
||
{
|
||
TrialId = data.TrialId,
|
||
SiteId = data.SiteId,
|
||
SubjectId = data.Id,
|
||
SubjectCode = data.Code,
|
||
IsSign = false,
|
||
CreateTime = createtime,
|
||
Identification = "Init|Subject|Status|Subject",
|
||
JsonDetail = JsonConvert.SerializeObject(new
|
||
{
|
||
Status = "新增",
|
||
})
|
||
});
|
||
}
|
||
await AddListInspectionRecordAsync(datas);
|
||
return entity;
|
||
|
||
}
|
||
|
||
|
||
#region 稽查
|
||
/// <summary>
|
||
/// 添加稽查记录
|
||
/// </summary>
|
||
/// <param name="datas"></param>
|
||
/// <returns></returns>
|
||
public async Task AddListInspectionRecordAsync(List<DataInspection> datas)
|
||
{
|
||
|
||
//var trialIds= datas.Select(x=>x.TrialId).Distinct().ToList();
|
||
//var subjectVisitIds= datas.Select(x=>x.SubjectVisitId).Distinct().ToList();
|
||
//var subjectIds = datas.Select(x => x.SubjectId).Distinct().ToList();
|
||
//var siteIds = datas.Select(x => x.SiteId).Distinct().ToList();
|
||
//var childrenTypes= datas.Select(x => x.ChildrenType).Distinct().ToList();
|
||
//var objectTypes = datas.Select(x => x.ObjectType).Distinct().ToList();
|
||
|
||
foreach (var add in datas)
|
||
{
|
||
await SetInspectionNameValue(add);
|
||
if (add.ParentId == null)
|
||
{
|
||
add.ParentId = (await _dbContext.DataInspection.AsQueryable().Where(x => x.TrialId == add.TrialId && x.SubjectVisitId == add.SubjectVisitId && x.SubjectId == add.SubjectId && x.SiteId == add.SiteId && x.ChildrenType == add.ChildrenType && x.ObjectType == add.ObjectType && x.VisitStageId == add.VisitStageId && x.GeneralId == add.GeneralId).OrderByDescending(x => x.CreateTime).FirstOrDefaultAsync())?.Id;
|
||
|
||
}
|
||
add.CreateUserId = _userInfo.Id;
|
||
add.IP = _userInfo.IP;
|
||
var JsonData = JsonConvert.DeserializeObject<IDictionary<string, object>>(add.JsonDetail);
|
||
|
||
foreach (var item in JsonData.Keys)
|
||
{
|
||
if (JsonData[item] == null)
|
||
{
|
||
continue;
|
||
}
|
||
if (JsonData[item].ToString().ToLower() == "true".ToLower())
|
||
{
|
||
JsonData[item] = "是";
|
||
}
|
||
else if (JsonData[item].ToString().ToLower() == "false".ToLower())
|
||
{
|
||
JsonData[item] = "否";
|
||
}
|
||
}
|
||
if (add.CreateTime == default(DateTime))
|
||
{
|
||
add.CreateTime = DateTime.Now;
|
||
}
|
||
add.JsonDetail = JsonConvert.SerializeObject(JsonData);
|
||
await SetDataInspectionDateType(add);
|
||
}
|
||
|
||
await _dbContext.DataInspection.AddRangeAsync(datas);
|
||
|
||
}
|
||
|
||
/// <summary>
|
||
/// IsNullOrEmpty
|
||
/// </summary>
|
||
/// <param name="value"></param>
|
||
/// <returns></returns>
|
||
private bool IsNullOrEmpty(object value)
|
||
{
|
||
if (value == null || value.ToString() == string.Empty)
|
||
{
|
||
return true;
|
||
}
|
||
else
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 格式化日期和时间
|
||
/// </summary>
|
||
/// <param name="Data"></param>
|
||
/// <returns></returns>
|
||
public async Task<DataInspection> SetDataInspectionDateType(DataInspection Data)
|
||
{
|
||
var list = await (from parent in _dbContext.FrontAuditConfig.AsQueryable().Where(x => x.Identification == Data.Identification)
|
||
join child in _dbContext.FrontAuditConfig.AsQueryable().Where(x => x.DateType != null && x.DateType != string.Empty) on parent.Id equals child.ParentId
|
||
select new DateDto()
|
||
{
|
||
Code= child.Code,
|
||
DateType= child.DateType,
|
||
}).ToListAsync();
|
||
|
||
var JsonData = JsonConvert.DeserializeObject<IDictionary<string, object>>(Data.JsonDetail);
|
||
|
||
foreach (var item in JsonData.Keys)
|
||
{
|
||
var datefirst = list.FirstOrDefault(x => x.Code.ToLower() == item.ToLower());
|
||
if (datefirst != null&& !IsNullOrEmpty(JsonData[item]))
|
||
{
|
||
try
|
||
{
|
||
if (datefirst.DateType == "Date")
|
||
{
|
||
JsonData[item] = DateTime.Parse(JsonData[item].ToString()).ToString("yyyy-MM-dd");
|
||
}
|
||
|
||
if (datefirst.DateType == "DateTime")
|
||
{
|
||
JsonData[item] = DateTime.Parse(JsonData[item].ToString()).ToString("yyyy-MM-dd HH:mm:ss");
|
||
}
|
||
|
||
}
|
||
catch (Exception)
|
||
{
|
||
|
||
continue;
|
||
}
|
||
}
|
||
|
||
|
||
|
||
}
|
||
Data.JsonDetail = JsonConvert.SerializeObject(JsonData);
|
||
|
||
return Data;
|
||
}
|
||
|
||
|
||
/// <summary>
|
||
/// 设置项目以及名称
|
||
/// </summary>
|
||
/// <param name="Data"></param>
|
||
/// <returns></returns>
|
||
public async Task SetInspectionNameValue(DataInspection Data)
|
||
{
|
||
#region 项目名称
|
||
|
||
var trialdata = await _dbContext.Trial.Select(x=>new { x.Id, x.ResearchProgramNo, x.ExperimentName, }).FirstOrDefaultAsync(x => x.Id == Data.TrialId);
|
||
Data.ResearchProgramNo = trialdata?.ResearchProgramNo;
|
||
if (IsNullOrEmpty(Data.TrialName))
|
||
{
|
||
Data.TrialName = trialdata?.ExperimentName;
|
||
}
|
||
#endregion
|
||
|
||
#region 测试中心名称
|
||
|
||
|
||
Data.SiteCode = (await _dbContext.TrialSite.IgnoreQueryFilters().FirstOrDefaultAsync(x => x.TrialId == Data.TrialId && x.SiteId == Data.SiteId))?.TrialSiteCode;
|
||
|
||
|
||
|
||
if (IsNullOrEmpty(Data.SiteName)&& Data.SiteId!=null)
|
||
{
|
||
var sitedata = await _dbContext.Site.Where(x => x.Id == Data.SiteId).Select(x=>new { x.SiteName}).FirstOrDefaultAsync();
|
||
Data.SiteName = sitedata?.SiteName;
|
||
}
|
||
#endregion
|
||
|
||
#region 受试者
|
||
|
||
|
||
if (IsNullOrEmpty(Data.SubjectCode) && Data.SubjectId != null)
|
||
{
|
||
|
||
Data.SubjectCode = (await _dbContext.Subject.Where(x => x.Id == Data.SubjectId).Select(x=>new {x.Code }).FirstOrDefaultAsync())?.Code;
|
||
}
|
||
#endregion
|
||
|
||
#region 访视
|
||
if (IsNullOrEmpty(Data.SubjectVisitName))
|
||
{
|
||
Data.SubjectVisitName = (await _dbContext.SubjectVisit.Where(x => x.Id == Data.SubjectVisitId).Select(x=>new { x.VisitName}).FirstOrDefaultAsync())?.VisitName;
|
||
}
|
||
#endregion
|
||
|
||
#region 创建者
|
||
|
||
if (IsNullOrEmpty(Data.CreateUserName))
|
||
{
|
||
Data.CreateUserName = _userInfo.RealName;
|
||
}
|
||
|
||
if (IsNullOrEmpty(Data.RoleName))
|
||
{
|
||
Data.RoleName = _userInfo.UserTypeShortName;
|
||
}
|
||
|
||
//if (IsNullOrEmpty(Data.CreateUserName) || IsNullOrEmpty(Data.RoleName))
|
||
//{
|
||
// var userdata = await _dbContext.Users.AsQueryable().Where(x => x.Id == Data.CreateUserId).GroupJoin(_dbContext.UserType.AsQueryable(), a => a.UserTypeId, b => b.Id, (a, b) => new
|
||
// {
|
||
// UserName = a.FirstName + a.LastName,
|
||
// Role = b
|
||
// }).SelectMany(a => a.Role, (m, n) => new
|
||
// {
|
||
// UserName = m.UserName,
|
||
// RoleName = n.UserTypeShortName
|
||
// }).FirstOrDefaultAsync();
|
||
|
||
// if (userdata != null)
|
||
// {
|
||
// if (IsNullOrEmpty(Data.CreateUserName))
|
||
// {
|
||
// Data.CreateUserName = userdata?.UserName;
|
||
// }
|
||
|
||
|
||
// if (IsNullOrEmpty(Data.RoleName))
|
||
// {
|
||
// Data.RoleName = userdata?.RoleName;
|
||
// }
|
||
// }
|
||
//}
|
||
#endregion
|
||
|
||
#region 取操作类型
|
||
try
|
||
{
|
||
var from = await _dbContext.FrontAuditConfig.FirstOrDefaultAsync(x => x.Identification == Data.Identification);
|
||
Data.ObjectType = from.ObjectTypeId;
|
||
Data.OptType = from.OptTypeId;
|
||
Data.ChildrenType = from.ChildrenTypeId;
|
||
Data.ModuleType = from.ModuleTypeId;
|
||
}
|
||
catch (Exception)
|
||
{
|
||
|
||
throw new BusinessValidationFailedException("操作标识异常");
|
||
}
|
||
|
||
|
||
#endregion
|
||
|
||
}
|
||
#endregion
|
||
|
||
/// <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 UpdatePartialFields(Guid id, Expression<Func<TEntity, TEntity>> updateFactory, bool autoSave = false, params EntityVerifyExp<TEntity>[] verify)
|
||
{
|
||
await SetPartialFieldUpdateAsync(id, updateFactory, verify);
|
||
|
||
await SaveChangesAsync(autoSave);
|
||
|
||
}
|
||
|
||
public async Task<bool> UpdatePartialFieldsNow(Guid id, Expression<Func<TEntity, TEntity>> updateFactory,
|
||
params EntityVerifyExp<TEntity>[] verify)
|
||
{
|
||
await SetPartialFieldUpdateAsync(id, updateFactory, verify);
|
||
return await SaveChangesAsync(true);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 部分字段更新,注意 模型标注了 ConcurrencyCheck的属性,这样的实体,不适合用部分字段更新,ef生成的更新sql会自动带上ConcurrencyCheck的属性条件
|
||
/// </summary>
|
||
/// <param name="id"></param>
|
||
/// <param name="updateFactory"></param>
|
||
/// <param name="verify"></param>
|
||
/// <returns></returns>
|
||
private async Task SetPartialFieldUpdateAsync(Guid id, Expression<Func<TEntity, TEntity>> updateFactory, 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;
|
||
}
|
||
|
||
#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<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> 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> 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> 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)
|
||
{
|
||
|
||
var bindings = ((MemberInitExpression)updateFactory.Body).Bindings.ToList();
|
||
|
||
if (typeof(IAuditUpdate).IsAssignableFrom(typeof(TEntity)))
|
||
{
|
||
var hasPropNameList = bindings.Select(t => t.Member.Name).ToList();
|
||
|
||
if (!hasPropNameList.Contains(nameof(IAuditUpdate.UpdateTime)))
|
||
{
|
||
bindings.Add(Expression.Bind(typeof(TEntity).GetMember(nameof(IAuditUpdate.UpdateTime))[0], Expression.Constant(DateTime.Now)));
|
||
|
||
}
|
||
|
||
if (!hasPropNameList.Contains( nameof(IAuditUpdate.UpdateUserId)))
|
||
{
|
||
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;
|
||
|
||
|
||
|
||
}
|
||
|
||
|
||
|
||
|
||
|
||
#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);
|
||
}
|
||
|
||
/// <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;
|
||
}
|
||
}
|
||
|
||
#endregion
|
||
}
|
||
|
||
}
|