修改仓储
parent
d4eda56a52
commit
f37783c91a
|
@ -40,67 +40,9 @@ namespace IRaCIS.Core.Infra.EFCore
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#region 异步部分
|
|
||||||
|
|
||||||
/// <summary>EF跟踪方式 添加</summary>
|
|
||||||
public async ValueTask<TEntity> AddAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
|
|
||||||
await _dbSet.AddAsync(entity).ConfigureAwait(false);
|
|
||||||
|
|
||||||
await AddInspectionAsync(entity);
|
|
||||||
|
|
||||||
if (autoSave)
|
|
||||||
{
|
|
||||||
await SaveChangesAsync(cancellationToken);
|
|
||||||
}
|
|
||||||
|
|
||||||
return entity;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private async Task AddInspectionAsync(TEntity entity)
|
#region 异步添加
|
||||||
{
|
|
||||||
List<DataInspection> datas = new List<DataInspection>();
|
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
private async Task EntityVerifyAsync(bool isAdd, EntityVerifyExp<TEntity>[] verify, Guid? entitydId = null)
|
||||||
|
@ -139,6 +81,36 @@ namespace IRaCIS.Core.Infra.EFCore
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public async Task<IEnumerable<TEntity>> AddRangeAsync(IEnumerable<TEntity> entities)
|
||||||
|
{
|
||||||
|
foreach (var addEntity in entities)
|
||||||
|
{
|
||||||
|
await AddAsync(addEntity);
|
||||||
|
}
|
||||||
|
|
||||||
|
return entities;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>EF跟踪方式 添加</summary>
|
||||||
|
public async ValueTask<TEntity> AddAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
|
||||||
|
await _dbSet.AddAsync(entity).ConfigureAwait(false);
|
||||||
|
|
||||||
|
await AddInspectionAsync(entity);
|
||||||
|
|
||||||
|
if (autoSave)
|
||||||
|
{
|
||||||
|
await SaveChangesAsync(cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
return entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public async Task<TEntity> InsertFromDTOAsync<TFrom>(TFrom from, bool autoSave = false, params EntityVerifyExp<TEntity>[] verify)
|
public async Task<TEntity> InsertFromDTOAsync<TFrom>(TFrom from, bool autoSave = false, params EntityVerifyExp<TEntity>[] verify)
|
||||||
{
|
{
|
||||||
var entity = _mapper.Map<TEntity>(from);
|
var entity = _mapper.Map<TEntity>(from);
|
||||||
|
@ -152,11 +124,376 @@ namespace IRaCIS.Core.Infra.EFCore
|
||||||
return entity;
|
return entity;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
#region 异步更新
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>用前端传递的视图模型字段,更新,同时返回数据库该条记录的原始信息,方便对比某些字段是否更改,进行相应的逻辑操作</summary>
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary> EF跟踪方式 生成 部分字段更新, 通过主键id 和表达式树 更新部分字段
|
||||||
|
/// 例如 Guid.Parse("8a90c96e-0776-4f7b-82a6-18933d339584"),u => new Dictionary() { ParentId = null, Code = "test" }默认会去处理更新更新人 更新时间</summary>
|
||||||
|
|
||||||
|
public async Task UpdatePartialFieldsAsync(Guid id, Expression<Func<TEntity, TEntity>> updateFactory, bool autoSave = false, params EntityVerifyExp<TEntity>[] verify)
|
||||||
|
{
|
||||||
|
await SetPartialFieldUpdateAsync(id, updateFactory, verify);
|
||||||
|
|
||||||
|
await SaveChangesAsync(autoSave);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary> EF跟踪方式 生成 部分字段立即更新,默认会去处理更新更新人 更新时间 </summary>
|
||||||
|
public async Task<bool> UpdatePartialFieldsNowAsync(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>
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
#region 异步 单个完整实体 EF 跟踪 自动生成 更新 和删除语句
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>EF跟踪方式 更新,全字段更新</summary>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>EF跟踪方式 删除</summary>
|
||||||
|
public async Task<bool> DeleteAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
_dbSet.Remove(entity);
|
||||||
|
|
||||||
|
if (autoSave)
|
||||||
|
{
|
||||||
|
return await SaveChangesAsync(cancellationToken);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 异步 批量删除和更新
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>批量删除,EF跟踪方式(所有查询出来,再删除 浪费性能,但是稽查 或者触发某些操作时,需要知道数据库实体信息 不可避免用这种)</summary>
|
||||||
|
public async Task TrackingBatchDeleteAsync(Expression<Func<TEntity, bool>> deleteFilter)
|
||||||
|
{
|
||||||
|
var waitDeleteList = await _dbSet.IgnoreQueryFilters().Where(deleteFilter).ToListAsync();
|
||||||
|
|
||||||
|
foreach (var deleteItem in waitDeleteList)
|
||||||
|
{
|
||||||
|
await DeleteAsync(deleteItem, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>批量删除,相当于原生sql, 没用EF跟踪方式(所有查询出来,再删除 浪费性能)</summary>
|
||||||
|
public async Task<bool> BatchDeleteAsync(Expression<Func<TEntity, bool>> deleteFilter)
|
||||||
|
{
|
||||||
|
return await _dbSet.IgnoreQueryFilters().Where(deleteFilter).BatchDeleteAsync() > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>批量更新,相当于原生sql, 没用EF跟踪方式(所有查询出来,再更新 浪费性能)</summary>
|
||||||
|
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 保存 和忽略
|
||||||
|
|
||||||
|
private async Task<bool> SaveChangesAsync(bool autoSave)
|
||||||
|
{
|
||||||
|
if (autoSave)
|
||||||
|
{
|
||||||
|
return await SaveChangesAsync();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>EF跟踪方式 生成 部分字段更新 (只更新传递的字段名 new[] {nameof(User.Name), nameof(User.Age))</summary>
|
||||||
|
public async Task<TEntity> UpdatePartialFieldsAsync(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>
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
#region 稽查
|
#region 稽查
|
||||||
|
|
||||||
|
|
||||||
|
private async Task AddInspectionAsync(TEntity entity)
|
||||||
|
{
|
||||||
|
List<DataInspection> datas = new List<DataInspection>();
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 添加稽查记录
|
/// 添加稽查记录
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -388,279 +725,8 @@ namespace IRaCIS.Core.Infra.EFCore
|
||||||
}
|
}
|
||||||
#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 UpdatePartialFieldsAsync(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> UpdatePartialFieldsNowAsync(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> UpdatePartialFieldsAsync(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 Task<IEnumerable<TEntity>> AddRangeAsync(IEnumerable<TEntity> entities)
|
|
||||||
{
|
|
||||||
foreach (var addEntity in entities)
|
|
||||||
{
|
|
||||||
await AddAsync(addEntity);
|
|
||||||
}
|
|
||||||
|
|
||||||
return entities;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#region 异步查询
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -694,102 +760,34 @@ namespace IRaCIS.Core.Infra.EFCore
|
||||||
return whereLambda == null ? await query.AsNoTracking().CountAsync() : await query.AsNoTracking().CountAsync(whereLambda);
|
return whereLambda == null ? await query.AsNoTracking().CountAsync() : await query.AsNoTracking().CountAsync(whereLambda);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>EF跟踪方式 更新,全字段更新</summary>
|
public async ValueTask<TEntity> FindAsync(Guid id, CancellationToken cancellationToken = default)
|
||||||
public async Task<bool> UpdateAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default)
|
|
||||||
{
|
{
|
||||||
_dbSet.Update(entity);
|
return await _dbContext.FindAsync<TEntity>(id);
|
||||||
|
|
||||||
if (autoSave)
|
|
||||||
{
|
|
||||||
return await SaveChangesAsync(cancellationToken);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//有可能是联合主键,本项目没用到,用Guid
|
||||||
/// <summary>EF跟踪方式 删除</summary>
|
public async ValueTask<TEntity> FindAsync(object[] keyValues, CancellationToken cancellationToken)
|
||||||
public async Task<bool> DeleteAsync(TEntity entity, bool autoSave = false, CancellationToken cancellationToken = default)
|
|
||||||
{
|
{
|
||||||
_dbSet.Remove(entity);
|
return await _dbContext.FindAsync<TEntity>(keyValues);
|
||||||
|
|
||||||
if (autoSave)
|
|
||||||
{
|
|
||||||
return await SaveChangesAsync(cancellationToken);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<TEntity> FirstOrDefaultAsync(Expression<Func<TEntity, bool>> exp = null, bool ignoreQueryFilters = false)
|
||||||
/// <summary>批量删除,EF跟踪方式(所有查询出来,再删除 浪费性能,但是稽查 或者触发某些操作时,需要知道数据库实体信息 不可避免用这种)</summary>
|
|
||||||
public async Task TrackingBatchDeleteAsync(Expression<Func<TEntity, bool>> deleteFilter)
|
|
||||||
{
|
|
||||||
var waitDeleteList = await _dbSet.IgnoreQueryFilters().Where(deleteFilter).ToListAsync();
|
|
||||||
|
|
||||||
foreach (var deleteItem in waitDeleteList)
|
|
||||||
{
|
|
||||||
await DeleteAsync(deleteItem, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>批量删除,相当于原生sql, 没用EF跟踪方式(所有查询出来,再删除 浪费性能)</summary>
|
|
||||||
public async Task<bool> BatchDeleteAsync(Expression<Func<TEntity, bool>> deleteFilter)
|
|
||||||
{
|
|
||||||
return await _dbSet.IgnoreQueryFilters().Where(deleteFilter).BatchDeleteAsync() > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>批量更新,相当于原生sql, 没用EF跟踪方式(所有查询出来,再更新 浪费性能)</summary>
|
|
||||||
public async Task<bool> BatchUpdateAsync(Expression<Func<TEntity, bool>> where,
|
|
||||||
Expression<Func<TEntity, TEntity>> updateFactory)
|
|
||||||
{
|
{
|
||||||
|
|
||||||
var bindings = ((MemberInitExpression)updateFactory.Body).Bindings.ToList();
|
var query = _dbSet.AsQueryable();
|
||||||
|
|
||||||
if (typeof(IAuditUpdate).IsAssignableFrom(typeof(TEntity)))
|
if (ignoreQueryFilters)
|
||||||
{
|
{
|
||||||
var hasPropNameList = bindings.Select(t => t.Member.Name).ToList();
|
query = query.IgnoreQueryFilters();
|
||||||
|
|
||||||
if (!hasPropNameList.Contains(nameof(IAuditUpdate.UpdateTime)))
|
|
||||||
{
|
|
||||||
bindings.Add(Expression.Bind(typeof(TEntity).GetMember(nameof(IAuditUpdate.UpdateTime))[0], Expression.Constant(DateTime.Now)));
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
if (exp == null)
|
||||||
if (!hasPropNameList.Contains(nameof(IAuditUpdate.UpdateUserId)))
|
return await query.FirstOrDefaultAsync().ConfigureAwait(false);
|
||||||
{
|
return await query.FirstOrDefaultAsync(exp).ConfigureAwait(false);
|
||||||
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
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#region 非异步部分
|
#region 非异步部分
|
||||||
|
|
||||||
public TEntity ImageFind(Guid id, Type type)
|
public TEntity ImageFind(Guid id, Type type)
|
||||||
|
@ -886,6 +884,11 @@ namespace IRaCIS.Core.Infra.EFCore
|
||||||
return _dbSet.AsNoTracking().ProjectTo<TDestination>(configuration, parameters, membersToExpand);
|
return _dbSet.AsNoTracking().ProjectTo<TDestination>(configuration, parameters, membersToExpand);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region 待废弃
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 仅仅供字典表插入使用,因为efcore 动态映射列的问题
|
/// 仅仅供字典表插入使用,因为efcore 动态映射列的问题
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -952,6 +955,9 @@ namespace IRaCIS.Core.Infra.EFCore
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue