简体   繁体   English

EF Core 3.1 过滤的通用表达式在 .Any(...) 上引发异常

[英]Generic expression for EF Core 3.1 filtering throws exception on .Any(…)

I try to create a generic way of getting data via EF Core 3.1 with the same filtering of different children.我尝试创建一种通过 EF Core 3.1 获取数据的通用方式,并对不同的子项进行相同的过滤。 For this, I try to extract the searched expression inside of Any(...).为此,我尝试在 Any(...) 中提取搜索到的表达式。

public Expression<Func<PraeparatUpdateEntity, bool>> IsToApprovePackungUpdates_Working()
{
   return entity => entity.PackungUpdates.Any(e => !e.IsImported
      && e.UpdateState != EntityUpdateState.Accepted
      && e.UpdateType != EntityUpdateType.Unchanged);
}

public Expression<Func<PraeparatUpdateEntity, bool>> IsToApprovePackungUpdates_NotWorking()
{
   var func = new Func<PackungUpdateEntity, bool>(e => !e.IsImported
      && e.UpdateState != EntityUpdateState.Accepted
      && e.UpdateType != EntityUpdateType.Unchanged);

   return entity => entity.PackungUpdates.Any(func);
}

public new async Task<ICollection<PraeparatUpdateEntity>> GetToApproveAsync(bool trackChanges = false)
{ 
   var query = Set.Include(praeparatUpdateEntity => praeparatUpdateEntity.PackungUpdates)
      .Where(IsToApprovePackungUpdates_NotWorking());
            
   if (!trackChanges)
   {
      query = query.AsNoTracking();
   }

   return await query.ToListAsync();
}

The first version is working.第一个版本正在运行。 The second one fails with errormessage:第二个失败并显示错误消息:

System.ArgumentException : Expression of type 'System.Func`2[MyProject.Data.Common.Entities.Update.PackungUpdateEntity,System.Boolean]' cannot be used for parameter of type 'System.Linq.Expressions.Expression`1[System.Func`2[MyProject.Data.Common.Entities.Update.PackungUpdateEntity,System.Boolean]]' of method 'Boolean Any[PackungUpdateEntity](System.Linq.IQueryable`1[MyProject.Data.Common.Entities.Update.PackungUpdateEntity], System.Linq.Expressions.Expression`1[System.Func`2[MyProject.Data.Common.Entities.Update.PackungUpdateEntity,System.Boolean]])' (Parameter 'arg1')
   at System.Dynamic.Utils.ExpressionUtils.ValidateOneArgument(MethodBase method, ExpressionType nodeKind, Expression arguments, ParameterInfo pi, String methodParamName, String argumentParamName, Int32 index)
   at System.Linq.Expressions.Expression.Call(Expression instance, MethodInfo method, Expression arg0, Expression arg1)
   at System.Linq.Expressions.Expression.Call(Expression instance, MethodInfo method, IEnumerable`1 arguments)
   at Microsoft.EntityFrameworkCore.Query.Internal.EnumerableToQueryableMethodConvertingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
   at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at System.Linq.Expressions.ExpressionVisitor.VisitLambda[T](Expression`1 node)
   at System.Linq.Expressions.Expression`1.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at System.Linq.Expressions.ExpressionVisitor.VisitUnary(UnaryExpression node)
   at System.Linq.Expressions.UnaryExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at System.Dynamic.Utils.ExpressionVisitorUtils.VisitArguments(ExpressionVisitor visitor, IArgumentProvider nodes)
   at System.Linq.Expressions.ExpressionVisitor.VisitMethodCall(MethodCallExpression node)
   at System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
   at System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
   at Microsoft.EntityFrameworkCore.Query.QueryTranslationPreprocessor.Process(Expression query)
   at Microsoft.EntityFrameworkCore.Query.QueryCompilationContext.CreateQueryExecutor[TResult](Expression query)
   at Microsoft.EntityFrameworkCore.Storage.Database.CompileQuery[TResult](Expression query, Boolean async)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCore[TResult](IDatabase database, Expression query, IModel model, Boolean async)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.<>c__DisplayClass12_0`1.<ExecuteAsync>b__0()
   at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQueryCore[TFunc](Object cacheKey, Func`1 compiler)
   at Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery[TResult](Object cacheKey, Func`1 compiler)
   at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteAsync[TResult](Expression query, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.ExecuteAsync[TResult](Expression expression, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable`1.GetAsyncEnumerator(CancellationToken cancellationToken)
   at System.Runtime.CompilerServices.ConfiguredCancelableAsyncEnumerable`1.GetAsyncEnumerator()
   at Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync[TSource](IQueryable`1 source, CancellationToken cancellationToken)
   at MyProject.Data.Repositories.PraeparatUpdateRepository.GetToApproveAsync(Boolean trackChanges) in C:\git\MyProject\Source\MyProject.Data\Repositories\PraeparatUpdateRepository.cs:line 156
   at MyProject.Data.Tests.Integration.RepositoryNavigationPropertyLoadingTests.GetAllPraeparatUpdates_WhereToApprove_WithNavigationProperties_OK_Test() in C:\git\MyProject\Source\MyProject.Data.Tests.Integration\RepositoryNavigationPropertyLoadingTests.cs:line 328
--- End of stack trace from previous location where exception was thrown ---

******** UPDATE ******** ******** 更新 ********

If I add AsQueryable() to my IEnumerable database children I can add my Expressions like that:如果我将 AsQueryable() 添加到我的 IEnumerable 数据库子项中,我可以像这样添加我的表达式:

var query = Set.Include(praeparatUpdateEntity => praeparatUpdateEntity.PackungUpdates)
   .Include(praeparatUpdateEntity => praeparatUpdateEntity.SequenzUpdates)
   .ThenInclude(sequenzUpdateEntity => sequenzUpdateEntity.ApplikationsartUpdates)
   .Include(praeparatUpdateEntity => praeparatUpdateEntity.SequenzUpdates)
   .ThenInclude(sequenzUpdateEntity => sequenzUpdateEntity.DeklarationUpdates)
   .Where(IsToApprove<PraeparatUpdateEntity>()
      .OrElse(entity => entity.PackungUpdates.AsQueryable().Any(IsToApprove<PackungUpdateEntity>()))
      .OrElse(entity => entity.SequenzUpdates.AsQueryable().Any(IsToApprove<SequenzUpdateEntity>()))
      .OrElse(entity => entity.SequenzUpdates.SelectMany(sequenzUpdateEntity => sequenzUpdateEntity.ApplikationsartUpdates).AsQueryable()
         .Any(IsToApprove<ApplikationsartUpdateEntity>()))
      .OrElse(entity => entity.SequenzUpdates.SelectMany(sequenzUpdateEntity => sequenzUpdateEntity.DeklarationUpdates).AsQueryable()
         .Any(IsToApprove<DeklarationUpdateEntity>())));

and my generic Expression:和我的通用表达式:

public Expression<Func<T, bool>> IsToApprove<T>() where T : class, IUpdateEntity
{
   return entity => !entity.IsImported && entity.UpdateState != EntityUpdateState.Accepted
   && entity.UpdateType != EntityUpdateType.Unchanged;
}

which seems at the moment to work... Tests in progress目前似乎可以正常工作......测试正在进行中

Entity Framework is build on top of IQueryable which is using expression trees .实体框架建立在使用表达式树IQueryable之上。 Those are required to analyze your code at runtime and transform it to SQL.这些是在运行时分析代码并将其转换为 SQL 所必需的。 In first snippet compiler takes all the heavy lifting of building expression trees for you.在第一个片段中,编译器为您承担了构建表达式树的所有繁重工作。 You can try building expression tree yourself, but usually it is not that simple task.您可以尝试自己构建表达式树,但通常不是那么简单的任务。 In this case you can try something like this:在这种情况下,您可以尝试以下操作:

Expression<Func<PackungUpdateEntity, bool>> exp = e => !e.IsImported
      && e.UpdateState != EntityUpdateState.Accepted
      && e.UpdateType != EntityUpdateType.Unchanged;

var any = typeof(Enumerable)
    .GetMethods()
    .Where(mi => mi.Name == nameof(Enumerable.Any) && mi.GetParameters().Length == 2)
    .Single()
    .MakeGenericMethod(typeof(PackungUpdateEntity));
var param = Expression.Parameter(typeof(PraeparatUpdateEntity));
var toAny = Expression.PropertyOrField(param, nameof(PraeparatUpdateEntity.PackungUpdates));
var call = Expression.Call(any, toAny, exp); 
var result = Expression.Lambda<Func<PraeparatUpdateEntity, bool>>(call, param);

return result;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM