解析 "InvalidOperationException: The LINQ expression [...] could not be translated" 并保持 ToListAsync()
Resolve "InvalidOperationException: The LINQ expression [...] could not be translated" and keeping the ToListAsync()
在 EF Core 3.x 无法翻译的 LINQ 查询不再在客户端进行评估。据我了解,这意味着它无法处理无法直接转换为 SQL.
的代码
在我的例子中,我想使用 foreach(PropertyInfo in type.GetProperties()) 来评估 class.
的每个组件
private static bool stringInMovement(Movement m, string toTest)
{
foreach(PropertyInfo component in typeof(Movement).GetProperties())
{
try {
if (component.GetValue(m).ToString().ToLower().Contains(toTest.ToLower()))
return true;
}
catch { }
}
return false;
}
然后在 where 语句中使用它:
movements = movements.Where(m=> stringInMovement(m, SearchString));
我想将结果保留为 IQueryable,因为在它所在的函数之后是一个异步任务,最后一行是
Movements = await movements.AsNoTracking().ToListAsync();
我想将所有函数保持为异步,并且我不想用 10x m.Component.Contains(searchstring) 编写 where 语句。顺便搜索一个数据库的特定元素。
感谢您的帮助!
我在尝试搜索内容时遇到的错误:
Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.<VisitMethodCall>g__CheckTranslated|8_0(ShapedQueryExpression translated, ref <>c__DisplayClass8_0 )
Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor)
System.Linq.Expressions.ExpressionVisitor.Visit(Expression node)
Microsoft.EntityFrameworkCore.Query.QueryCompilationContext.CreateQueryExecutor<TResult>(Expression query)
Microsoft.EntityFrameworkCore.Storage.Database.CompileQuery<TResult>(Expression query, bool async)
Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCore<TResult>(IDatabase database, Expression query, IModel model, bool async)
Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler+<>c__DisplayClass12_0<TResult>.<ExecuteAsync>b__0()
Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQueryCore<TFunc>(object cacheKey, Func<Func<QueryContext, TFunc>> compiler)
Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery<TResult>(object cacheKey, Func<Func<QueryContext, TResult>> compiler)
Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteAsync<TResult>(Expression query, CancellationToken cancellationToken)
Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken)
Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable<TResult>.GetAsyncEnumerator(CancellationToken cancellationToken)
System.Runtime.CompilerServices.ConfiguredCancelableAsyncEnumerable<T>.GetAsyncEnumerator()
Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync<TSource>(IQueryable<TSource> source, CancellationToken cancellationToken)
Intuo.IndexModel.OnGetAsync() in Index.cshtml.cs
+
Movements = await movements.AsNoTracking().ToListAsync();
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.ExecutorFactory+NonGenericTaskHandlerMethod.Execute(object receiver, object[] arguments)
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.InvokeHandlerMethodAsync()
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.InvokeNextPageFilterAsync()
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.Rethrow(PageHandlerExecutedContext context)
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.InvokeInnerFilterAsync()
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
您想要的是动态生成一个 Expression<Lambda>
以在 where 子句中使用。
public static Expression<Func<T,bool>> ContainsString<T>(string value)
{
var constValue = Expression.Constant(value);
var parameter = Expression.Parameter(typeof(T), "p");
return Expression.Lambda<Func<T, bool>>(
typeof(T).GetProperties()
.Where(p => p.PropertyType == typeof(string))
.Select(p => (Expression)Expression.Call(
Expression.Property(parameter, p),
"Contains",
new Type[] { typeof(string) },
constValue))
.Aggregate((a, c) => Expression.OrElse(a, c)),
parameter);
}
movements = movements.Where(ContainsString<Movement>(SearchString));
EF/EF 核心无法将涉及反射或更复杂逻辑的任何内容翻译成 SQL。取而代之的是,有几种方法可以实现所需的 - 在查询中写入所有道具:
movements = movements
.Where(m => m.Property1.ToString().ToLower().Contains(SearchString.ToString().ToLower() &&
m.Property2.ToString().ToLower().Contains(SearchString.ToString().ToLower() /* ... */ );
或构建特定的 Expression
,它将作为参数通过自定义 Expression Visitor 或方法传递给 .Where
:
public static Expression<Func<Movement, bool>> CompareToStr(string searchString)
{
Expression res = null;
var param = Expression.Parameter(typeof(Movement), "x");
foreach (var component in typeof(Movement).GetProperties())
{
// building the expression to get a property
var arg = Expression.Property(param, component.Name);
// now we have `x.Property1` expression
var toStrCall = Expression.Call(
// to what expression we applying the .ToString method
arg,
// link to 'ToString',
// needed to be altered, if it would be used in non-sql runtime as if there are nullable types with `null` values, this would cause NRE at runtime
component.PropertyType.GetMethod(nameof(object.ToString), new Type[] { }));
// now we have `x.Property1.ToString()` (watch out NRE)
var toLowerCall = Expression.Call(
toStrCall,
typeof(string).GetMethod(nameof(string.ToLower), new Type[] { }));
// now we have `x.Property1.ToString().ToLower()`
var containsCall = Expression.Call(
toLowerCall,
typeof(string).GetMethod(nameof(string.Contains), new[] { typeof(string) }),
Expression.Constant(searchString.ToLower())); // since arguments of expression tree should be the expressions
// here we passed the constant string expression, so now we have
// x.Property1.ToString().ToLower().Contains( value of testString.ToLower())
if (res == null)
{
res = containsCall;
}
else
{
res = Expression.Or(res, containsCall);
}
// after several iterations it has
// x.Property1...Contains(testString) || x.Property2...Contains(testString) and so on
}
return Expression.Lambda<Func<Movement, bool>>(res, param);
// and result x => x.Property1... || x.Property2 ...
}
(我没有测试过这段代码)。所以目标是创建 Expression Tree 的实例并将其作为参数传递给 .Where
调用。这样 EF/EF 核心将能够将其翻译成 SQL,因为它知道如何翻译 .ToString()
调用和 .ToLower()
调用:
movements = movements.Where(CompareToStr(SearchString));
在 EF Core 3.x 无法翻译的 LINQ 查询不再在客户端进行评估。据我了解,这意味着它无法处理无法直接转换为 SQL.
的代码在我的例子中,我想使用 foreach(PropertyInfo in type.GetProperties()) 来评估 class.
的每个组件private static bool stringInMovement(Movement m, string toTest)
{
foreach(PropertyInfo component in typeof(Movement).GetProperties())
{
try {
if (component.GetValue(m).ToString().ToLower().Contains(toTest.ToLower()))
return true;
}
catch { }
}
return false;
}
然后在 where 语句中使用它:
movements = movements.Where(m=> stringInMovement(m, SearchString));
我想将结果保留为 IQueryable,因为在它所在的函数之后是一个异步任务,最后一行是
Movements = await movements.AsNoTracking().ToListAsync();
我想将所有函数保持为异步,并且我不想用 10x m.Component.Contains(searchstring) 编写 where 语句。顺便搜索一个数据库的特定元素。
感谢您的帮助!
我在尝试搜索内容时遇到的错误:
Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.<VisitMethodCall>g__CheckTranslated|8_0(ShapedQueryExpression translated, ref <>c__DisplayClass8_0 ) Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression) Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression) System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor visitor) System.Linq.Expressions.ExpressionVisitor.Visit(Expression node) Microsoft.EntityFrameworkCore.Query.QueryCompilationContext.CreateQueryExecutor<TResult>(Expression query) Microsoft.EntityFrameworkCore.Storage.Database.CompileQuery<TResult>(Expression query, bool async) Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCore<TResult>(IDatabase database, Expression query, IModel model, bool async) Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler+<>c__DisplayClass12_0<TResult>.<ExecuteAsync>b__0() Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQueryCore<TFunc>(object cacheKey, Func<Func<QueryContext, TFunc>> compiler) Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOrAddQuery<TResult>(object cacheKey, Func<Func<QueryContext, TResult>> compiler) Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteAsync<TResult>(Expression query, CancellationToken cancellationToken) Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken) Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable<TResult>.GetAsyncEnumerator(CancellationToken cancellationToken) System.Runtime.CompilerServices.ConfiguredCancelableAsyncEnumerable<T>.GetAsyncEnumerator() Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListAsync<TSource>(IQueryable<TSource> source, CancellationToken cancellationToken) Intuo.IndexModel.OnGetAsync() in Index.cshtml.cs + Movements = await movements.AsNoTracking().ToListAsync(); Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.ExecutorFactory+NonGenericTaskHandlerMethod.Execute(object receiver, object[] arguments) Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.InvokeHandlerMethodAsync() Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.InvokeNextPageFilterAsync() Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.Rethrow(PageHandlerExecutedContext context) Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted) Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.InvokeInnerFilterAsync() Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|24_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, object state, bool isCompleted) Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context) Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted) Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync() Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope) Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger) Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context) Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
您想要的是动态生成一个 Expression<Lambda>
以在 where 子句中使用。
public static Expression<Func<T,bool>> ContainsString<T>(string value)
{
var constValue = Expression.Constant(value);
var parameter = Expression.Parameter(typeof(T), "p");
return Expression.Lambda<Func<T, bool>>(
typeof(T).GetProperties()
.Where(p => p.PropertyType == typeof(string))
.Select(p => (Expression)Expression.Call(
Expression.Property(parameter, p),
"Contains",
new Type[] { typeof(string) },
constValue))
.Aggregate((a, c) => Expression.OrElse(a, c)),
parameter);
}
movements = movements.Where(ContainsString<Movement>(SearchString));
EF/EF 核心无法将涉及反射或更复杂逻辑的任何内容翻译成 SQL。取而代之的是,有几种方法可以实现所需的 - 在查询中写入所有道具:
movements = movements
.Where(m => m.Property1.ToString().ToLower().Contains(SearchString.ToString().ToLower() &&
m.Property2.ToString().ToLower().Contains(SearchString.ToString().ToLower() /* ... */ );
或构建特定的 Expression
,它将作为参数通过自定义 Expression Visitor 或方法传递给 .Where
:
public static Expression<Func<Movement, bool>> CompareToStr(string searchString)
{
Expression res = null;
var param = Expression.Parameter(typeof(Movement), "x");
foreach (var component in typeof(Movement).GetProperties())
{
// building the expression to get a property
var arg = Expression.Property(param, component.Name);
// now we have `x.Property1` expression
var toStrCall = Expression.Call(
// to what expression we applying the .ToString method
arg,
// link to 'ToString',
// needed to be altered, if it would be used in non-sql runtime as if there are nullable types with `null` values, this would cause NRE at runtime
component.PropertyType.GetMethod(nameof(object.ToString), new Type[] { }));
// now we have `x.Property1.ToString()` (watch out NRE)
var toLowerCall = Expression.Call(
toStrCall,
typeof(string).GetMethod(nameof(string.ToLower), new Type[] { }));
// now we have `x.Property1.ToString().ToLower()`
var containsCall = Expression.Call(
toLowerCall,
typeof(string).GetMethod(nameof(string.Contains), new[] { typeof(string) }),
Expression.Constant(searchString.ToLower())); // since arguments of expression tree should be the expressions
// here we passed the constant string expression, so now we have
// x.Property1.ToString().ToLower().Contains( value of testString.ToLower())
if (res == null)
{
res = containsCall;
}
else
{
res = Expression.Or(res, containsCall);
}
// after several iterations it has
// x.Property1...Contains(testString) || x.Property2...Contains(testString) and so on
}
return Expression.Lambda<Func<Movement, bool>>(res, param);
// and result x => x.Property1... || x.Property2 ...
}
(我没有测试过这段代码)。所以目标是创建 Expression Tree 的实例并将其作为参数传递给 .Where
调用。这样 EF/EF 核心将能够将其翻译成 SQL,因为它知道如何翻译 .ToString()
调用和 .ToLower()
调用:
movements = movements.Where(CompareToStr(SearchString));