【发布时间】:2020-08-31 09:55:36
【问题描述】:
我尝试创建一种通过 EF Core 3.1 获取数据的通用方式,并对不同的子项进行相同的过滤。为此,我尝试在 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();
}
第一个版本正在运行。 第二个失败并显示错误消息:
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 ---
********更新********
如果我将 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>())));
和我的通用表达式:
public Expression<Func<T, bool>> IsToApprove<T>() where T : class, IUpdateEntity
{
return entity => !entity.IsImported && entity.UpdateState != EntityUpdateState.Accepted
&& entity.UpdateType != EntityUpdateType.Unchanged;
}
目前看来可行...正在进行测试
【问题讨论】:
-
If I add AsQueryable() to my IEnumerable database children他们已经是IQueryable,而不是IEnumerable。DbSet<T>实现了IQueryable<T>,这就是您可以使用 LINQ 的原因。问题是您的“通用表达式”实际上是使用Any的特定函数调用,无法转换为 SQL。真正起作用的是特定实体上的表达式。你想做什么?不管它是什么不需要这么复杂的表达方式 -
您是否要过滤子实体?您不能在 EF Core 3.1 中执行此操作,它将在 EF Core 5 中提供。过滤子实体不与在查询中应用所有过滤器相同,在之后JOIN 产生大量空行或重复行。您需要过滤 JOIN 子句本身,这在 EF Core 3.1 中是不可能的。在 SQL 中,您需要
parent LEFT JOIN (select ... from child where child.IsApproved =10 c on c.ParentID=parent.ID -
鉴于所有这些条件都是硬编码的,您可以创建仅返回匹配对象并将您的实体映射到它们的 SQL Server 视图或函数
-
我正在尝试获取 PraeparateUpdateEntities 列表(包含所有子项),其中需要批准实体本身或其任何子项的八倍。所以我不需要过滤任何子实体。
标签: c# ef-core-3.1