464 lines
15 KiB
C#
464 lines
15 KiB
C#
|
||
|
||
using AutoMapper;
|
||
using IRaCIS.Core.Domain.Models;
|
||
using IRaCIS.Core.Infrastructure;
|
||
using IRaCIS.Core.Infrastructure.Extention;
|
||
using Microsoft.EntityFrameworkCore;
|
||
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
||
using System;
|
||
using System.Collections.Generic;
|
||
using System.Linq;
|
||
using System.Linq.Expressions;
|
||
using System.Threading.Tasks;
|
||
using IRaCIS.Core.Domain.Share;
|
||
using EFCore.BulkExtensions;
|
||
using Microsoft.Extensions.Localization;
|
||
|
||
namespace IRaCIS.Core.Infra.EFCore
|
||
{
|
||
|
||
#region 泛型通用版本
|
||
|
||
|
||
public interface IRepository
|
||
{
|
||
IQueryable<T> GetQueryable<T>(bool ignoreQueryFilters = false) where T : Entity;
|
||
DbSet<T> Set<T>() where T : Entity;
|
||
EntityEntry<T> Entry<T>(T t) where T : Entity;
|
||
|
||
Task<bool> SaveChangesAsync();
|
||
|
||
IQueryable<T> WhereIf<T>(bool condition, Expression<Func<T, bool>> filter) where T : Entity;
|
||
|
||
|
||
Task<bool> AnyAsync<T>(Expression<Func<T, bool>> filter, bool ignoreQueryFilters = false) where T : Entity;
|
||
|
||
/// <summary>
|
||
///不跟踪 查询单个实体,不会出现NUll
|
||
/// </summary>
|
||
/// <param name="exp"></param>
|
||
/// <param name="ignoreQueryFilters"></param>
|
||
/// <returns></returns>
|
||
Task<T> FirstAsync<T>(Expression<Func<T, bool>> exp = null, bool isTracking = false, bool ignoreQueryFilters = false) where T : Entity;
|
||
|
||
Task<T> FirstOrDefaultAsync<T>(Expression<Func<T, bool>> exp = null, bool ignoreQueryFilters = false) where T : Entity;
|
||
Task<int> CountAsync<T>(Expression<Func<T, bool>> exp = null, bool ignoreQueryFilters = false) where T : Entity;
|
||
|
||
IQueryable<T> Where<T>(Expression<Func<T, bool>> exp = null, bool isTraking = false, bool ignoreQueryFilters = false) where T : Entity;
|
||
|
||
ValueTask<T> FindAsync<T>(Guid id) where T : Entity;
|
||
|
||
|
||
|
||
Task<T> InsertOrUpdateAsync<T, TFrom>(TFrom from, bool autoSave = false, params EntityVerifyExp<T>[] verify) where T : Entity;
|
||
|
||
Task<T> InsertFromDTOAsync<T, TFrom>(TFrom from, bool autoSave = false, params EntityVerifyExp<T>[] verify) where T : Entity;
|
||
|
||
Task<T> UpdateFromDTOAsync<T, TFrom>(TFrom from, bool autoSave = false, bool ignoreDtoNullProperty = true, params EntityVerifyExp<T>[] verify) where T : Entity;
|
||
|
||
|
||
|
||
ValueTask<T> AddAsync<T>(T entity, bool autoSave = false) where T : Entity;
|
||
|
||
Task UpdateRange<T>(IEnumerable<T> entities, bool autoSave = false) where T : Entity;
|
||
Task<bool> AddRangeAsync<T>(IEnumerable<T> entities, bool autoSave = false) where T : Entity;
|
||
|
||
Task<bool> UpdateAsync<T>(T entity, bool autoSave = false) where T : Entity;
|
||
|
||
Task<bool> DeleteAsync<T>(T entity, bool autoSave = false) where T : Entity;
|
||
|
||
|
||
Task<bool> BatchDeleteAsync<T>(Expression<Func<T, bool>> deleteFilter) where T : Entity;
|
||
|
||
Task<bool> BatchUpdateAsync<T>(Expression<Func<T, bool>> where, Expression<Func<T, T>> updateFactory) where T : Entity;
|
||
|
||
Task UpdatePartialFromQueryAsync<T>(Expression<Func<T, bool>> updateFilter,
|
||
Expression<Func<T, T>> updateFactory,
|
||
bool autoSave = false, bool ignoreQueryFilter = false) where T : Entity;
|
||
}
|
||
|
||
public class Repository : IRepository
|
||
{
|
||
#region 构造 基本
|
||
private IRaCISDBContext _dbContext { get; }
|
||
public IMapper _mapper { get; set; }
|
||
|
||
public IUserInfo _userInfo { get; set; }
|
||
|
||
public IStringLocalizer _localizer { get; set; }
|
||
|
||
public Repository(IRaCISDBContext dbContext, IMapper mapper, IUserInfo userInfo, IStringLocalizer localizer)
|
||
{
|
||
_localizer = localizer;
|
||
_dbContext = dbContext;
|
||
_mapper = mapper;
|
||
_userInfo = userInfo;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置是使用哪个仓储 默认不跟踪
|
||
/// </summary>
|
||
/// <typeparam name="T"></typeparam>
|
||
/// <param name="isTraking"></param>
|
||
/// <returns></returns>
|
||
|
||
public IQueryable<T> GetQueryable<T>(bool ignoreQueryFilters = false) where T : Entity
|
||
{
|
||
IQueryable<T> query = _dbContext.Set<T>();
|
||
|
||
if (ignoreQueryFilters)
|
||
{
|
||
query = query.IgnoreQueryFilters();
|
||
}
|
||
return query.AsNoTracking();
|
||
}
|
||
public DbSet<T> Set<T>() where T : Entity
|
||
{
|
||
return _dbContext.Set<T>();
|
||
}
|
||
|
||
public EntityEntry<T> Entry<T>(T t) where T : Entity
|
||
{
|
||
return _dbContext.Entry<T>(t);
|
||
}
|
||
public IQueryable<T> WhereIf<T>(bool condition, Expression<Func<T, bool>> filter) where T : Entity
|
||
{
|
||
IQueryable<T> query = _dbContext.Set<T>().AsNoTracking();
|
||
return condition ? query.Where(filter) : query;
|
||
}
|
||
|
||
private async Task<bool> SaveChangesAsync(bool autoSave)
|
||
{
|
||
if (autoSave)
|
||
{
|
||
return await SaveChangesAsync();
|
||
}
|
||
else
|
||
{
|
||
return false;
|
||
}
|
||
}
|
||
|
||
public async Task<bool> SaveChangesAsync()
|
||
{
|
||
|
||
return await _dbContext.SaveChangesAsync().ConfigureAwait(false) > 0;
|
||
|
||
}
|
||
|
||
#endregion
|
||
|
||
|
||
|
||
|
||
public async Task<T> InsertFromDTOAsync<T, TFrom>(TFrom from, bool autoSave = false,
|
||
params EntityVerifyExp<T>[] verify) where T : Entity
|
||
{
|
||
|
||
var entity = _mapper.Map<T>(from);
|
||
|
||
await _dbContext.EntityVerifyAsync(true, verify);
|
||
|
||
entity = await AddAsync(entity, autoSave);
|
||
|
||
|
||
return entity;
|
||
}
|
||
|
||
public async Task<T> UpdateFromDTOAsync<T, TFrom>(TFrom from, bool autoSave = false, bool ignoreDtoNullProperty = true, params EntityVerifyExp<T>[] verify) where T : Entity
|
||
{
|
||
var entity = _mapper.Map<T>(from);
|
||
|
||
|
||
await _dbContext.EntityVerifyAsync(false, verify, entity.Id);
|
||
|
||
var dbEntity = await _dbContext.Set<T>().IgnoreQueryFilters().FirstOrDefaultAsync(t => t.Id == entity.Id).ConfigureAwait(false);
|
||
|
||
if (dbEntity == null)
|
||
{
|
||
|
||
throw new BusinessValidationFailedException(_localizer["Repository_UpdateError"]);
|
||
}
|
||
|
||
var dbBeforEntity = dbEntity.Clone();
|
||
|
||
|
||
_mapper.Map(from, dbEntity);
|
||
|
||
|
||
//DTO null 属性不更新 防止意外操作,导致保存数据错误,或者 add 和update 用一个模型,更新的时候,只传递了部分字段,导致,不想更新的字段,因为没传递值,用null覆盖了
|
||
// Guid?属性 为null 时 映射到 Guid 时 默认会变成 Guid.Empty
|
||
if (ignoreDtoNullProperty)
|
||
{
|
||
var dbEntityProp = typeof(T).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;
|
||
}
|
||
|
||
|
||
|
||
public async Task<T> InsertOrUpdateAsync<T, TFrom>(TFrom from, bool autoSave = false, params EntityVerifyExp<T>[] verify) where T : Entity
|
||
{
|
||
var entity = _mapper.Map<T>(from);
|
||
|
||
|
||
if (entity.Id == Guid.Empty)
|
||
{
|
||
return await InsertFromDTOAsync(from, autoSave, verify);
|
||
}
|
||
else
|
||
{
|
||
return await UpdateFromDTOAsync(from, autoSave, false, verify);
|
||
}
|
||
}
|
||
|
||
|
||
public async Task<bool> AnyAsync<T>(Expression<Func<T, bool>> filter, bool ignoreQueryFilters = false) where T : Entity
|
||
{
|
||
|
||
var query = _dbContext.Set<T>().AsQueryable();
|
||
|
||
if (ignoreQueryFilters)
|
||
{
|
||
query = query.IgnoreQueryFilters();
|
||
}
|
||
|
||
return await query.AsNoTracking().AnyAsync(filter).ConfigureAwait(false);
|
||
|
||
}
|
||
|
||
|
||
|
||
|
||
#region 基本查询 异步
|
||
|
||
/// <summary>
|
||
/// 跟踪查询某个实体 --异步 默认是跟踪查询
|
||
/// </summary>
|
||
/// <typeparam name="T"></typeparam>
|
||
/// <param name="exp"></param>
|
||
/// <returns></returns>
|
||
///
|
||
public async Task<int> CountAsync<T>(Expression<Func<T, bool>> exp = null, bool ignoreQueryFilters = false) where T : Entity
|
||
{
|
||
var query = _dbContext.Set<T>().AsQueryable();
|
||
|
||
if (ignoreQueryFilters)
|
||
{
|
||
query = query.IgnoreQueryFilters();
|
||
}
|
||
|
||
if (exp != null)
|
||
{
|
||
query = query.Where(exp);
|
||
}
|
||
|
||
return await query.CountAsync().ConfigureAwait(false);
|
||
|
||
|
||
}
|
||
|
||
public async Task<T> FirstAsync<T>(Expression<Func<T, bool>> exp = null, bool isTracking = false, bool ignoreQueryFilters = false) where T : Entity
|
||
{
|
||
|
||
var query = _dbContext.Set<T>().AsQueryable();
|
||
|
||
if (!isTracking)
|
||
{
|
||
query = query.AsNoTracking();
|
||
}
|
||
|
||
if (ignoreQueryFilters)
|
||
{
|
||
query = query.IgnoreQueryFilters();
|
||
}
|
||
|
||
var entity = await query.FirstOrDefaultAsync();
|
||
|
||
if (entity is null)
|
||
{
|
||
throw new QueryBusinessObjectNotExistException($"The query object {typeof(T).Name} does not exist in database, Please check the query parameters");
|
||
}
|
||
else
|
||
{
|
||
return entity;
|
||
}
|
||
|
||
}
|
||
public async Task<T> FirstOrDefaultAsync<T>(Expression<Func<T, bool>> exp = null, bool ignoreQueryFilters = false) where T : Entity
|
||
{
|
||
|
||
var query = _dbContext.Set<T>().AsQueryable();
|
||
|
||
if (ignoreQueryFilters)
|
||
{
|
||
query = query.IgnoreQueryFilters();
|
||
}
|
||
|
||
if (exp != null)
|
||
{
|
||
query = query.Where(exp);
|
||
}
|
||
|
||
return await query.FirstOrDefaultAsync().ConfigureAwait(false);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 过滤 默认不跟踪
|
||
/// </summary>
|
||
/// <typeparam name="T"></typeparam>
|
||
/// <param name="exp"></param>
|
||
/// <param name="isTraking"></param>
|
||
/// <returns></returns>
|
||
public IQueryable<T> Where<T>(Expression<Func<T, bool>> exp = null, bool isTraking = false, bool ignoreQueryFilters = false) where T : Entity
|
||
{
|
||
IQueryable<T> query = _dbContext.Set<T>();
|
||
|
||
if (ignoreQueryFilters)
|
||
{
|
||
query = query.IgnoreQueryFilters();
|
||
}
|
||
|
||
if (!isTraking)
|
||
{
|
||
query = query.AsNoTracking();
|
||
}
|
||
|
||
if (exp != null)
|
||
{
|
||
query = query.Where(exp);
|
||
}
|
||
return query;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 首先在内存中查找,其次再找数据库
|
||
/// </summary>
|
||
/// <typeparam name="T"></typeparam>
|
||
/// <param name="id"></param>
|
||
/// <returns></returns>
|
||
public async ValueTask<T> FindAsync<T>(Guid id) where T : Entity
|
||
{
|
||
return await _dbContext.Set<T>().FindAsync(id).ConfigureAwait(false);
|
||
}
|
||
|
||
#endregion
|
||
|
||
#region 基本添加、更新、删除 异步
|
||
|
||
public async ValueTask<T> AddAsync<T>(T entity, bool autoSave = false) where T : Entity
|
||
{
|
||
|
||
await _dbContext.Set<T>().AddAsync(entity).ConfigureAwait(false);
|
||
|
||
await SaveChangesAsync(autoSave);
|
||
|
||
return entity;
|
||
}
|
||
|
||
public async Task<bool> AddRangeAsync<T>(IEnumerable<T> entities, bool autoSave = false) where T : Entity
|
||
{
|
||
await _dbContext.Set<T>().AddRangeAsync(entities).ConfigureAwait(false);
|
||
|
||
return await SaveChangesAsync(autoSave);
|
||
}
|
||
|
||
public async Task UpdateRange<T>(IEnumerable<T> entities, bool autoSave = false) where T : Entity
|
||
{
|
||
_dbContext.Set<T>().UpdateRange(entities);
|
||
|
||
await SaveChangesAsync(autoSave);
|
||
|
||
}
|
||
|
||
public async Task<bool> UpdateAsync<T>(T entity, bool autoSave = false) where T : Entity
|
||
{
|
||
_dbContext.Set<T>().Update(entity);
|
||
|
||
return await SaveChangesAsync(autoSave);
|
||
}
|
||
|
||
|
||
public async Task<bool> DeleteAsync<T>(T entity, bool autoSave = false) where T : Entity
|
||
{
|
||
_dbContext.Set<T>().Remove(entity);
|
||
|
||
return await SaveChangesAsync(autoSave);
|
||
|
||
}
|
||
|
||
public async Task<bool> DeleteManyAsync<T>(IEnumerable<T> entities, bool autoSave = false) where T : Entity
|
||
{
|
||
_dbContext.Set<T>().RemoveRange(entities);
|
||
|
||
return await SaveChangesAsync(autoSave);
|
||
}
|
||
|
||
#endregion
|
||
|
||
public async Task<bool> BatchDeleteAsync<T>(Expression<Func<T, bool>> deleteFilter) where T : Entity
|
||
{
|
||
return await _dbContext.BatchDeleteNoTrackingAsync(deleteFilter);
|
||
}
|
||
|
||
public async Task<bool> BatchUpdateAsync<T>(Expression<Func<T, bool>> whereFilter, Expression<Func<T, T>> updateFactory) where T : Entity
|
||
{
|
||
|
||
return await _dbContext.BatchUpdateNoTrackingAsync(whereFilter, updateFactory, _userInfo.Id);
|
||
|
||
//return await _dbContext.Set<T>().IgnoreQueryFilters().Where(whereFilter).BatchUpdateAsync(updateFactory).ConfigureAwait(false) > 0;
|
||
|
||
}
|
||
|
||
|
||
public async Task UpdatePartialFromQueryAsync<T>(Expression<Func<T, bool>> updateFilter,
|
||
Expression<Func<T, T>> updateFactory,
|
||
bool autoSave = false, bool ignoreQueryFilter = false) where T : Entity
|
||
{
|
||
if (updateFilter == null)
|
||
{
|
||
throw new ArgumentException("更新过滤条件不允许为空", nameof(updateFilter));
|
||
}
|
||
var query = ignoreQueryFilter ? _dbContext.Set<T>().AsNoTracking().IgnoreQueryFilters() : _dbContext.Set<T>().AsNoTracking();
|
||
|
||
var searchEntityList = await query.Where(updateFilter).ToListAsync();
|
||
|
||
foreach (var needUpdateEntity in searchEntityList)
|
||
{
|
||
_dbContext.EntityModifyPartialFiled(needUpdateEntity, updateFactory);
|
||
}
|
||
|
||
await SaveChangesAsync(autoSave);
|
||
}
|
||
|
||
public async Task<bool> UpdateAsync<T>(T waitModifyEntity, Expression<Func<T, T>> updateFactory, bool autoSave = false) where T : Entity
|
||
{
|
||
|
||
_dbContext.EntityModifyPartialFiled(waitModifyEntity, updateFactory);
|
||
|
||
return await SaveChangesAsync(autoSave);
|
||
|
||
}
|
||
|
||
|
||
}
|
||
|
||
#endregion
|
||
|
||
|
||
|
||
|
||
}
|