ABP 在 EntityFramework 中使用扩展实现批量操作
注意:目前批量删除即使已继承 ISoftDelete 的情况下依然会物理删除,所以可以通过批量更新实现逻辑删除。
调用方法:
await projectsRepository.BatchUpdateAsync(x => new Projects { Status = 2 }, x => projectIdList.Contains(x.Id));
以下是批量更新与删除的内部实现。
批量删除:
/// <summary>
/// Deletes all matching entities permanently for given predicate
/// </summary>
/// <typeparam name="TEntity">Entity type</typeparam>
/// <typeparam name="TPrimaryKey">Primary key type</typeparam>
/// <param name="repository">Repository</param>
/// <param name="predicate">Predicate to filter entities</param>
/// <returns></returns>
public static async Task<int> BatchDeleteAsync<TEntity, TPrimaryKey>([NotNull] this IRepository<TEntity, TPrimaryKey> repository, [NotNull] Expression<Func<TEntity, bool>> predicate)
where TEntity : Entity<TPrimaryKey>
{
Check.NotNull(repository, nameof(repository));
Check.NotNull(predicate, nameof(predicate));
var query = repository.GetAll().IgnoreQueryFilters();
var abpFilterExpression = GetFilterExpressionOrNull<TEntity, TPrimaryKey>(repository.GetIocResolver());
var filterExpression = ExpressionCombiner.Combine(predicate, abpFilterExpression);
query = query.Where(filterExpression);
return await query.DeleteAsync();
}
批量更新:
/// <summary>
/// Updates all matching entities using given updateExpression for given predicate
/// </summary>
/// <typeparam name="TEntity">Entity type</typeparam>
/// <typeparam name="TPrimaryKey">Primary key type</typeparam>
/// <param name="repository">Repository</param>
/// /// <param name="updateExpression">Update expression</param>
/// <param name="predicate">Predicate to filter entities</param>
/// <returns></returns>
public static async Task<int> BatchUpdateAsync<TEntity, TPrimaryKey>([NotNull]this IRepository<TEntity, TPrimaryKey> repository, [NotNull]Expression<Func<TEntity, TEntity>> updateExpression, [NotNull]Expression<Func<TEntity, bool>> predicate)
where TEntity : Entity<TPrimaryKey>
{
Check.NotNull(repository, nameof(repository));
Check.NotNull(updateExpression, nameof(updateExpression));
Check.NotNull(predicate, nameof(predicate));
var query = repository.GetAll().IgnoreQueryFilters();
var abpFilterExpression = GetFilterExpressionOrNull<TEntity, TPrimaryKey>(repository.GetIocResolver());
var filterExpression = ExpressionCombiner.Combine(predicate, abpFilterExpression);
query = query.Where(filterExpression);
return await query.UpdateAsync(updateExpression);
}
ABP 扩展类内部实现:
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;
using Abp.Dependency;
using Abp.Domain.Entities;
using Abp.Domain.Repositories;
using Abp.Domain.Uow;
using Abp.Linq.Expressions;
using Abp.Runtime.Session;
using JetBrains.Annotations;
using Microsoft.EntityFrameworkCore;
using Z.EntityFramework.Plus;
namespace Abp.EntityFrameworkCore.EFPlus
{
/// <summary>
/// Defines batch delete and update extension methods for IRepository
/// </summary>
public static class AbpEntityFrameworkCoreEfPlusExtensions
{
/// <summary>
/// Deletes all matching entities permanently for given predicate
/// </summary>
/// <typeparam name="TEntity">Entity type</typeparam>
/// <typeparam name="TPrimaryKey">Primary key type</typeparam>
/// <param name="repository">Repository</param>
/// <param name="predicate">Predicate to filter entities</param>
/// <returns></returns>
public static async Task<int> BatchDeleteAsync<TEntity, TPrimaryKey>([NotNull] this IRepository<TEntity, TPrimaryKey> repository, [NotNull] Expression<Func<TEntity, bool>> predicate)
where TEntity : Entity<TPrimaryKey>
{
Check.NotNull(repository, nameof(repository));
Check.NotNull(predicate, nameof(predicate));
var query = repository.GetAll().IgnoreQueryFilters();
var abpFilterExpression = GetFilterExpressionOrNull<TEntity, TPrimaryKey>(repository.GetIocResolver());
var filterExpression = ExpressionCombiner.Combine(predicate, abpFilterExpression);
query = query.Where(filterExpression);
return await query.DeleteAsync();
}
/// <summary>
/// Deletes all matching entities permanently for given predicate
/// </summary>
/// <typeparam name="TEntity">Entity type</typeparam>
/// <param name="repository">Repository</param>
/// <param name="predicate">Predicate to filter entities</param>
/// <returns></returns>
public static async Task<int> BatchDeleteAsync<TEntity>([NotNull] this IRepository<TEntity> repository, [NotNull]Expression<Func<TEntity, bool>> predicate)
where TEntity : Entity<int>
{
return await repository.BatchDeleteAsync<TEntity, int>(predicate);
}
/// <summary>
/// Updates all matching entities using given updateExpression for given predicate
/// </summary>
/// <typeparam name="TEntity">Entity type</typeparam>
/// <typeparam name="TPrimaryKey">Primary key type</typeparam>
/// <param name="repository">Repository</param>
/// /// <param name="updateExpression">Update expression</param>
/// <param name="predicate">Predicate to filter entities</param>
/// <returns></returns>
public static async Task<int> BatchUpdateAsync<TEntity, TPrimaryKey>([NotNull]this IRepository<TEntity, TPrimaryKey> repository, [NotNull]Expression<Func<TEntity, TEntity>> updateExpression, [NotNull]Expression<Func<TEntity, bool>> predicate)
where TEntity : Entity<TPrimaryKey>
{
Check.NotNull(repository, nameof(repository));
Check.NotNull(updateExpression, nameof(updateExpression));
Check.NotNull(predicate, nameof(predicate));
var query = repository.GetAll().IgnoreQueryFilters();
var abpFilterExpression = GetFilterExpressionOrNull<TEntity, TPrimaryKey>(repository.GetIocResolver());
var filterExpression = ExpressionCombiner.Combine(predicate, abpFilterExpression);
query = query.Where(filterExpression);
return await query.UpdateAsync(updateExpression);
}
/// <summary>
/// Updates all matching entities using given updateExpression for given predicate
/// </summary>
/// <typeparam name="TEntity">Entity type</typeparam>
/// <param name="repository">Repository</param>
/// /// <param name="updateExpression">Update expression</param>
/// <param name="predicate">Predicate to filter entities</param>
/// <returns></returns>
public static async Task<int> BatchUpdateAsync<TEntity>(
[NotNull]this IRepository<TEntity> repository, [NotNull]Expression<Func<TEntity, TEntity>> updateExpression,
[NotNull]Expression<Func<TEntity, bool>> predicate)
where TEntity : Entity<int>
{
return await repository.BatchUpdateAsync<TEntity, int>(updateExpression, predicate);
}
private static Expression<Func<TEntity, bool>> GetFilterExpressionOrNull<TEntity, TPrimaryKey>(IIocResolver iocResolver) where TEntity : Entity<TPrimaryKey>
{
Expression<Func<TEntity, bool>> expression = null;
using (var scope = iocResolver.CreateScope())
{
var currentUnitOfWorkProvider = scope.Resolve<ICurrentUnitOfWorkProvider>();
if (typeof(ISoftDelete).IsAssignableFrom(typeof(TEntity)))
{
var isSoftDeleteFilterEnabled = currentUnitOfWorkProvider.Current?.IsFilterEnabled(AbpDataFilters.SoftDelete) == true;
if (isSoftDeleteFilterEnabled)
{
Expression<Func<TEntity, bool>> softDeleteFilter = e => !((ISoftDelete)e).IsDeleted;
expression = softDeleteFilter;
}
}
if (typeof(IMayHaveTenant).IsAssignableFrom(typeof(TEntity)))
{
var isMayHaveTenantFilterEnabled = currentUnitOfWorkProvider.Current?.IsFilterEnabled(AbpDataFilters.MayHaveTenant) == true;
var currentTenantId = GetCurrentTenantIdOrNull(iocResolver);
if (isMayHaveTenantFilterEnabled)
{
Expression<Func<TEntity, bool>> mayHaveTenantFilter = e => ((IMayHaveTenant)e).TenantId == currentTenantId;
expression = expression == null ? mayHaveTenantFilter : ExpressionCombiner.Combine(expression, mayHaveTenantFilter);
}
}
if (typeof(IMustHaveTenant).IsAssignableFrom(typeof(TEntity)))
{
var isMustHaveTenantFilterEnabled = currentUnitOfWorkProvider.Current?.IsFilterEnabled(AbpDataFilters.MustHaveTenant) == true;
var currentTenantId = GetCurrentTenantIdOrNull(iocResolver);
if (isMustHaveTenantFilterEnabled)
{
Expression<Func<TEntity, bool>> mustHaveTenantFilter = e => ((IMustHaveTenant)e).TenantId == currentTenantId;
expression = expression == null ? mustHaveTenantFilter : ExpressionCombiner.Combine(expression, mustHaveTenantFilter);
}
}
}
return expression;
}
private static int? GetCurrentTenantIdOrNull(IIocResolver iocResolver)
{
using (var scope = iocResolver.CreateScope())
{
var currentUnitOfWorkProvider = scope.Resolve<ICurrentUnitOfWorkProvider>();
if (currentUnitOfWorkProvider?.Current != null)
{
return currentUnitOfWorkProvider.Current.GetTenantId();
}
return iocResolver.Resolve<IAbpSession>().TenantId;
}
}
}
}