mirror of https://github.com/abpframework/abp.git
Browse Source
Add `HasAbpQueryFilter` method to combine the custom filter with global filters.pull/17156/head
committed by
GitHub
9 changed files with 160 additions and 38 deletions
@ -0,0 +1,27 @@ |
|||
using System; |
|||
using System.Linq.Expressions; |
|||
using Microsoft.EntityFrameworkCore.Metadata.Builders; |
|||
using Microsoft.EntityFrameworkCore.Metadata.Internal; |
|||
|
|||
namespace Volo.Abp.EntityFrameworkCore; |
|||
|
|||
public static class EntityTypeBuilderExtensions |
|||
{ |
|||
/// <summary>
|
|||
/// This method is used to add a query filter to this entity which combine with ABP EF Core builtin query filters.
|
|||
/// </summary>
|
|||
/// <returns></returns>
|
|||
public static EntityTypeBuilder<TEntity> HasAbpQueryFilter<TEntity>(this EntityTypeBuilder<TEntity> builder, Expression<Func<TEntity, bool>> filter) |
|||
where TEntity : class |
|||
{ |
|||
#pragma warning disable EF1001
|
|||
var queryFilterAnnotation = builder.Metadata.FindAnnotation(CoreAnnotationNames.QueryFilter); |
|||
#pragma warning restore EF1001
|
|||
if (queryFilterAnnotation != null && queryFilterAnnotation.Value != null && queryFilterAnnotation.Value is Expression<Func<TEntity, bool>> existingFilter) |
|||
{ |
|||
filter = QueryFilterExpressionHelper.CombineExpressions(filter, existingFilter); |
|||
} |
|||
|
|||
return builder.HasQueryFilter(filter); |
|||
} |
|||
} |
|||
@ -0,0 +1,37 @@ |
|||
using System; |
|||
using System.Linq.Expressions; |
|||
|
|||
namespace Volo.Abp.EntityFrameworkCore; |
|||
|
|||
public static class QueryFilterExpressionHelper |
|||
{ |
|||
public static Expression<Func<T, bool>> CombineExpressions<T>(Expression<Func<T, bool>> expression1, Expression<Func<T, bool>> expression2) |
|||
{ |
|||
var parameter = Expression.Parameter(typeof(T)); |
|||
|
|||
var leftVisitor = new ReplaceExpressionVisitor(expression1.Parameters[0], parameter); |
|||
var left = leftVisitor.Visit(expression1.Body); |
|||
|
|||
var rightVisitor = new ReplaceExpressionVisitor(expression2.Parameters[0], parameter); |
|||
var right = rightVisitor.Visit(expression2.Body); |
|||
|
|||
return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(left!, right!), parameter); |
|||
} |
|||
|
|||
private class ReplaceExpressionVisitor : ExpressionVisitor |
|||
{ |
|||
private readonly Expression _oldValue; |
|||
private readonly Expression _newValue; |
|||
|
|||
public ReplaceExpressionVisitor(Expression oldValue, Expression newValue) |
|||
{ |
|||
_oldValue = oldValue; |
|||
_newValue = newValue; |
|||
} |
|||
|
|||
public override Expression Visit(Expression node) |
|||
{ |
|||
return node == _oldValue ? _newValue : base.Visit(node); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,36 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Shouldly; |
|||
using Volo.Abp.Data; |
|||
using Volo.Abp.Domain.Repositories; |
|||
using Volo.Abp.TestApp.Domain; |
|||
using Volo.Abp.TestApp.Testing; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.EntityFrameworkCore.DataFiltering; |
|||
|
|||
public class EfCore_Custom_Filter_Tests : TestAppTestBase<AbpEntityFrameworkCoreTestModule> |
|||
{ |
|||
private readonly IBasicRepository<Category, Guid> _categoryRepository; |
|||
|
|||
public EfCore_Custom_Filter_Tests() |
|||
{ |
|||
_categoryRepository = GetRequiredService<IBasicRepository<Category, Guid>>(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task Should_Combine_Abp_And_Custom_QueryFilter_Test() |
|||
{ |
|||
var categories = await _categoryRepository.GetListAsync(); |
|||
categories.Count.ShouldBe(1); |
|||
categories[0].Name.ShouldBe("abp.cli"); |
|||
|
|||
using (GetRequiredService<IDataFilter<ISoftDelete>>().Disable()) |
|||
{ |
|||
categories = await _categoryRepository.GetListAsync(); |
|||
categories.Count.ShouldBe(2); |
|||
categories.ShouldContain(x => x.Name == "abp.cli" && x.IsDeleted == false); |
|||
categories.ShouldContain(x => x.Name == "abp.core" && x.IsDeleted == true); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,11 @@ |
|||
using System; |
|||
using Volo.Abp.Domain.Entities; |
|||
|
|||
namespace Volo.Abp.TestApp.Domain; |
|||
|
|||
public class Category : AggregateRoot<Guid>, ISoftDelete |
|||
{ |
|||
public string Name { get; set; } |
|||
|
|||
public bool IsDeleted { get; set; } |
|||
} |
|||
Loading…
Reference in new issue