C# “解决”;InvalidOperationException:无法翻译LINQ表达式[…];以及保持ToListSync()

C# “解决”;InvalidOperationException:无法翻译LINQ表达式[…];以及保持ToListSync(),c#,asp.net,linq,asp.net-core,ef-core-3.0,C#,Asp.net,Linq,Asp.net Core,Ef Core 3.0,在EF Core 3.x中,无法翻译的LINQ查询不再在客户端上计算。据我所知,这意味着它无法处理无法直接转换为SQL的代码 在我的例子中,我想使用foreach(type.GetProperties()中的PropertyInfo)来计算类的每个组件 private static bool stringInMovement(Movement m, string toTest) { foreach(PropertyInfo component in typeof(Movement).G

在EF Core 3.x中,无法翻译的LINQ查询不再在客户端上计算。据我所知,这意味着它无法处理无法直接转换为SQL的代码


在我的例子中,我想使用foreach(type.GetProperties()中的PropertyInfo)来计算类的每个组件

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();
我希望将all函数保持为异步函数,并且不希望必须使用10x m.Component.Contains(searchstring)编写where语句。顺便说一下,它是用来搜索数据库中的特定元素的

谢谢你的帮助

尝试搜索某些内容时出现的错误:

Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslationExpressionVisitor.g_uCheckTranslated | 8_0(ShapedQueryExpression translated,参考c_uDisplayClass8_0)
Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslationExpressionVisitor.VisitMethodCall(MethodCallExpression MethodCallExpression)
Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslationExpressionVisitor.VisitMethodCallExpression(MethodCallExpression MethodCallExpression)
System.Linq.Expressions.MethodCallExpression.Accept(ExpressionVisitor)
System.Linq.Expressions.ExpressionVisitor.Visit(表达式节点)
Microsoft.EntityFrameworkCore.Query.QueryCompilationContext.CreateQueryExecutor(表达式查询)
Microsoft.EntityFrameworkCore.Storage.Database.CompileQuery(表达式查询,bool异步)
Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.CompileQueryCore(IDatabase数据库、表达式查询、IModel模型、bool异步)
Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler+c\u显示类12\u 0.b\u 0()
Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOradQueryCore(对象缓存键,Func编译器)
Microsoft.EntityFrameworkCore.Query.Internal.CompiledQueryCache.GetOradQuery(对象缓存键,Func编译器)
Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.ExecuteAsync(表达式查询,取消令牌取消令牌)
Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.ExecuteAsync(表达式表达式,CancellationToken CancellationToken)
Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryable.GetAsyncEnumerator(CancellationToken CancellationToken)
System.Runtime.CompilerServices.ConfiguredCancelableAsyncEnumerable.GetAsyncEnumerator()
Microsoft.EntityFrameworkCore.EntityFrameworkQueryableExtensions.ToListSync(IQueryable源,CancellationToken CancellationToken)
Index.cshtml.cs中的Intuo.IndexModel.OnGetAsync()
+
移动=等待移动。AsNoTracking().toListSync();
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.ExecutorFactory+NonGenericTaskHandlerMethod.Execute(对象接收器,对象[]参数)
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.InvokeHandlerMethodAsync()
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.InvokeNextPageFilterAsync()
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.Rethrow(PageHandlerExecutedContext)
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.Next(ref状态Next,ref作用域,ref对象状态,ref bool已完成)
Microsoft.AspNetCore.Mvc.RazorPages.Infrastructure.PageActionInvoker.InvokeInnerFilterAsync()
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g_|24_0(ResourceInvoker invoker,Task lastTask,State next,Scope Scope,object State,bool isCompleted)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed上下文)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(ref状态Next、ref作用域、ref对象状态、ref bool已完成)
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.InvokeFilterPipelineAsync()
Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.g|u waiting | 17|0(ResourceInvoker invoker,Task Task,IDisposable作用域)
Microsoft.AspNetCore.Routing.EndpointMiddleware.g_uwaitRequestTask | 6_0(端点端点、任务请求任务、ILogger记录器)
Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext上下文)
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext上下文)

您想要的是动态生成一个
表达式
,以便在where子句中使用

公共静态表达式包含字符串(字符串值)
{
var constValue=表达式常数(值);
var参数=表达式参数(类型为(T),“p”);
返回表达式.Lambda(
typeof(T).GetProperties()
.Where(p=>p.PropertyType==typeof(字符串))
.选择(p=>(表达式)Expression.Call(
表达式.Property(参数p),
“包含”,
新类型[]{typeof(string)},
常量值)
.Aggregate((a,c)=>Expression.OrElse(a,c)),
参数);
}
移动=移动。其中(包含字符串(搜索字符串));

EF/EF核心无法将任何涉及反射或更复杂逻辑的内容转换为SQL。与此相反,有几种方法可以实现所需的功能—在查询中编写所有道具:

movements = movements
    .Where(m => m.Property1.ToString().ToLower().Contains(SearchString.ToString().ToLower() &&
                m.Property2.ToString().ToLower().Contains(SearchString.ToString().ToLower() /* ... */ );
或者构建特定的
表达式
,该表达式将作为参数传递到
。其中
通过自定义或使用方法:

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 ...
}

@Matomat
ToString
方法是在
object
类中定义的,因此应该可以调用
ToStr
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 ...
}
movements = movements.Where(CompareToStr(SearchString));