我有一个基本过滤器类,它负责检查实体集合是否与其子类中定义的过滤器条件匹配,如:
public abstract class FilterInputBase<TEntity> : IFilterInput<TEntity>
{
public Expression<Func<TEntity, bool>> MatchesFilter()
{
var input = Expression.Variable(typeof(TEntity), "entity");
var expressions = GetFilterExpressions().ToList();
if(expressions.Count == 0)
expressions.Add(e => true);
// checks if the input satisfies all the filter conditions
var resultExpression = expressions.Aggregate(
(l, r) => Expression.Lambda<Func<TEntity, bool>>(
Expression.AndAlso(Expression.Invoke(l, input), Expression.Invoke(r, input)), input));
return resultExpression;
}
/// <summary>
/// Returns a list of filter conditions converted into Expressions
/// </summary>
/// <returns></returns>
protected abstract IEnumerable<Expression<Func<TEntity, bool>>> GetFilterExpressions();
}
此类被用作:
public static IQueryable<TEntity> Filter<TEntity>(this IQueryable<TEntity> query, IFilterInput<TEntity> filterInput) =>
query.Where(filterInput?.MatchesFilter() ?? (x => true));
顾名思义,GetFilterExpressions
方法返回一组基于筛选器参数的筛选器表达式。这些过滤条件在MatchesFilter
方法中聚合以形成单个过滤表达式:resultExpression
。
此解决方案最多可使用两个筛选器表达式。现在我需要两个以上的表达式。但如果有更多的表达式,则查询在第二次尝试时失败。它适用于第一次运行。我得到了以下错误,添加了更多的表达式:
"message": "An item with the same key has already been added. Key: entity",
"stackTrace": " at System.Collections.Generic.Dictionary`2.TryInsert(TKey key, TValue value, InsertionBehavior behavior)n at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value)n at Microsoft.EntityFrameworkCore.Query.ExpressionEqualityComparer.ExpressionComparer.CompareLambda(LambdaExpression a, LambdaExpression b)n at Microsoft.EntityFrameworkCore.Query.ExpressionEqualityComparer.ExpressionComparer.Compare(Expression left, Expression right)n at Microsoft.EntityFrameworkCore.Query.ExpressionEqualityComparer.ExpressionComparer.CompareInvocation(InvocationExpression a, InvocationExpression b)n at Microsoft.EntityFrameworkCore.Query.ExpressionEqualityComparer.ExpressionComparer.Compare(Expression left, Expression right)n at Microsoft.EntityFrameworkCore.Query.ExpressionEqualityComparer.ExpressionComparer.CompareBinary(BinaryExpression a, BinaryExpression b)n at Microsoft.EntityFrameworkCore.Query.ExpressionEqualityComparer.ExpressionComparer.Compare(Expression left, Expression right)n at Microsoft.EntityFrameworkCore.Query.ExpressionEqualityComparer.ExpressionComparer.CompareLambda(LambdaExpression a, LambdaExpression b)n at Microsoft.EntityFrameworkCore.Query.ExpressionEqualityComparer.ExpressionComparer.Compare(Expression left, Expression right)n at Microsoft.EntityFrameworkCore.Query.ExpressionEqualityComparer.ExpressionComparer.CompareUnary(UnaryExpression a, UnaryExpression b)n at Microsoft.EntityFrameworkCore.Query.ExpressionEqualityComparer.ExpressionComparer.Compare(Expression left, Expression right)n at Microsoft.EntityFrameworkCore.Query.ExpressionEqualityComparer.ExpressionComparer.CompareExpressionList(IReadOnlyList`1 a, IReadOnlyList`1 b)n at Microsoft.EntityFrameworkCore.Query.ExpressionEqualityComparer.ExpressionComparer.CompareMethodCall(MethodCallExpression a, MethodCallExpression b)n at Microsoft.EntityFrameworkCore.Query.ExpressionEqualityComparer.ExpressionComparer.Compare(Expression left, Expression right)n at Microsoft.EntityFrameworkCore.Query.ExpressionEqualityComparer.ExpressionComparer.CompareExpressionList(IReadOnlyList`1 a, IReadOnlyList`1 b)n at Microsoft.EntityFrameworkCore.Query.ExpressionEqualityComparer.ExpressionComparer.CompareMethodCall(MethodCallExpression a, MethodCallExpression b)n at Microsoft.EntityFrameworkCore.Query.ExpressionEqualityComparer.ExpressionComparer.Compare(Expression left, Expression right)n at Microsoft.EntityFrameworkCore.Query.ExpressionEqualityComparer.Equals(Expression x, Expression y)n at Microsoft.EntityFrameworkCore.Query.CompiledQueryCacheKeyGenerator.CompiledQueryCacheKey.Equals(CompiledQueryCacheKey other)n at Microsoft.EntityFrameworkCore.Query.RelationalCompiledQueryCacheKeyGenerator.RelationalCompiledQueryCacheKey.Equals(RelationalCompiledQueryCacheKey other)n at Microsoft.EntityFrameworkCore.SqlServer.Query.Internal.SqlServerCompiledQueryCacheKeyGenerator.SqlServerCompiledQueryCacheKey.Equals(SqlServerCompiledQueryCacheKey other)n at Microsoft.EntityFrameworkCore.SqlServer.Query.Internal.SqlServerCompiledQueryCacheKeyGenerator.SqlServerCompiledQueryCacheKey.Equals(Object obj)n at System.Collections.Concurrent.ConcurrentDictionary`2.TryGetValueInternal(TKey key, Int32 hashcode, TValue& value)n at System.Collections.Concurrent.ConcurrentDictionary`2.TryGetValue(TKey key, TValue& value)n at Microsoft.Extensions.Caching.Memory.MemoryCache.TryGetValue(Object key, Object& result)n at Microsoft.Extensions.Caching.Memory.CacheExtensions.TryGetValue[TItem](IMemoryCache cache, Object key, TItem& value)n at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery[TResult](Object cacheKey, Func`1 compiler)n at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteAsync[TResult](Expression query, CancellationToken cancellationToken)n at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.ExecuteAsync[TResult](Expression expression, CancellationToken cancellationToken)n at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1.GetAsyncEnumerator(CancellationToken cancellationToken)n at System.Runtime.CompilerServices.ConfiguredCancelableAsyncEnumerable`1.GetAsyncEnumerator()n at HotChocolate.Types.Pagination.QueryableOffsetPagingHandler`1.ExecuteQueryableAsync(IQueryable`1 queryable, CancellationToken cancellationToken)n at HotChocolate.Types.Pagination.QueryableOffsetPagingHandler`1.ResolveAsync(IResolverContext context, IQueryable`1 queryable, OffsetPagingArguments arguments)n at HotChocolate.Types.Pagination.OffsetPagingHandler.HotChocolate.Types.Pagination.IPagingHandler.SliceAsync(IResolverContext context, Object source)n at HotChocolate.Types.Pagination.PagingMiddleware.InvokeAsync(IMiddlewareContext context)n at HotChocolate.Utilities.MiddlewareCompiler`1.ExpressionHelper.AwaitTaskHelper(Task task)n at HotChocolate.Data.ToListMiddleware`1.InvokeAsync(IMiddlewareContext context)n at HotChocolate.Types.EntityFrameworkObjectFieldDescriptorExtensions.<>c__DisplayClass2_1`1.<<UseDbContext>b__4>d.MoveNext()n--- End of stack trace from previous location where exception was thrown ---n at HotChocolate.Types.EntityFrameworkObjectFieldDescriptorExtensions.<>c__DisplayClass2_1`1.<<UseDbContext>b__4>d.MoveNext()n--- End of stack trace from previous location where exception was thrown ---n at HotChocolate.AspNetCore.Authorization.AuthorizeMiddleware.InvokeAsync(IDirectiveContext context)n at HotChocolate.Utilities.MiddlewareCompiler`1.ExpressionHelper.AwaitTaskHelper(Task task)n at HotChocolate.Execution.Processing.ResolverTask.ExecuteResolverPipelineAsync(CancellationToken cancellationToken)n at HotChocolate.Execution.Processing.ResolverTask.TryExecuteAsync(CancellationToken cancellationToken)"
我认为expressions.Aggregate()
可能不是正确的方法。有更好的方法吗?此外,我需要清除任何缓存的词典吗?
您可以递归地创建BinaryExpressions,然后用更多的BinaryExpressions将它们包装起来
例如,您有以下内容:
x => x > 1
x => x == 2
x => x < 3
x => x != 4
我们基本上取最后两个表达式,并从中创建一个二进制表达式
var lastExpression = Expression.MakeBinary(ExpressionType.AndAlso,
expression3, expression4);
然后添加第二个和第一个
lastExpression = Expression.MakeBinary(ExpressionType.AndAlso,
expression2, lastExpression);
lastExpression = Expression.MakeBinary(ExpressionType.AndAlso,
expression1, lastExpression);
通过这种方式,您将创建类似以下表达式的内容:
x => (x > 1 && (x == 2 && (x < 3 && x != 4)))
你只需要调用
Expression.invoke(lastExpression, input);
如果有人遇到类似的问题,下面是我找到的对我有效的解决方案
public Expression<Func<TEntity, bool>> MatchesFilter()
{
var expressions = GetFilterExpressions().ToList();
if (expressions.Count <= 1)
return expressions.FirstOrDefault() ?? (x => true);
var input = Expression.Parameter(typeof(TEntity), "x");
var resultExpression = Expression.AndAlso(Expression.Invoke(expressions[0], input), Expression.Invoke(expressions[1], input));
resultExpression = expressions.Skip(2).Aggregate(resultExpression, (current, expression) =>
Expression.AndAlso(current, Expression.Invoke(expression, input)));
return Expression.Lambda<Func<TEntity, bool>>(resultExpression, input);
}