逐步构建 lambda 表达式时出现问题
Issue when building lambda expression progressively
我正在尝试以这种方式逐步构建 lambda 表达式:
public class PropertySearchFilter
{
public virtual Expression<Func<T,bool>> GetSearchFilter<T>(SearchFilterModel filterModelModel) where T: Property
{
Expression<Func<T, bool>> combinedFilter = null;
Expression<Func<T, bool>>? countryFilter = filterModelModel.CountryId.HasValue ? x => x.CountryId == filterModelModel.CountryId.GetValueOrDefault() : null;
Expression<Func<T, bool>>? cityFilter = filterModelModel.CityId.HasValue ? x => x.CityId == filterModelModel.CityId.GetValueOrDefault() : null;
Expression<Func<T, bool>>? categoryFilter = filterModelModel.CategoryId.HasValue ? x => x.CategoryId == filterModelModel.CategoryId.GetValueOrDefault() : null;
Expression<Func<T, bool>>? transactionTypeFilter = filterModelModel.TransactionTypeId.HasValue
? x => x.TransactionTypeId == filterModelModel.TransactionTypeId.GetValueOrDefault()
: null;
Expression<Func<T, bool>>? publicFilter = filterModelModel.IsPublic.HasValue ? x => x.IsPublic == filterModelModel.IsPublic.GetValueOrDefault() : null;
Expression<Func<T, bool>>? createdByFilter = !string.IsNullOrEmpty(filterModelModel.CreatedBy) ? x => x.CreatedBy == filterModelModel.CreatedBy : null;
Expression<Func<T, bool>>? minimumPriceFilter =
filterModelModel.MinimumPrice.HasValue ? x => x.Price >= filterModelModel.MinimumPrice.GetValueOrDefault() : null;
Expression<Func<T, bool>>? maximumPriceFilter = filterModelModel.MaximumPrice.HasValue ? x => x.Price <= filterModelModel.MaximumPrice.GetValueOrDefault() : null;
if (countryFilter != null)
combinedFilter = countryFilter;
if (cityFilter != null)
{
combinedFilter = combinedFilter.And(cityFilter);
}
if (categoryFilter != null)
{
combinedFilter = combinedFilter.And(categoryFilter);
}
if (transactionTypeFilter != null)
{
combinedFilter = combinedFilter.And(transactionTypeFilter);
}
if (publicFilter != null)
{
combinedFilter = combinedFilter.And(publicFilter);
}
if (createdByFilter != null)
{
combinedFilter = combinedFilter.And(createdByFilter);
}
if (minimumPriceFilter != null)
{
combinedFilter = combinedFilter.And(minimumPriceFilter);
}
if (maximumPriceFilter != null)
{
combinedFilter = combinedFilter.And(maximumPriceFilter);
}
return combinedFilter;
}
}
这是扩展方法:
public static class ExpressionExtensionsMethods
{
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> left, Expression<Func<T, bool>> right)
{
if (left == null) return right;
var and = Expression.AndAlso(left.Body, right.Body);
return Expression.Lambda<Func<T, bool>>(and, left.Parameters.Single());
}
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> left, Expression<Func<T, bool>> right)
{
if (left == null) return right;
var and = Expression.OrElse(left.Body, right.Body);
return Expression.Lambda<Func<T, bool>>(and, left.Parameters.Single());
}
}
我收到以下错误:
System.InvalidOperationException: The LINQ expression 'x' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
at Microsoft.EntityFrameworkCore.Query.RelationalSqlTranslatingExpressionVisitor.VisitParameter(ParameterExpression parameterExpression)
at Microsoft.EntityFrameworkCore.Query.RelationalSqlTranslatingExpressionVisitor.VisitMember(MemberExpression memberExpression)
at Microsoft.EntityFrameworkCore.Query.RelationalSqlTranslatingExpressionVisitor.VisitBinary(BinaryExpression binaryExpression)
at Microsoft.EntityFrameworkCore.SqlServer.Query.Internal.SqlServerSqlTranslatingExpressionVisitor.VisitBinary(BinaryExpression binaryExpression)
at Microsoft.EntityFrameworkCore.Query.RelationalSqlTranslatingExpressionVisitor.VisitBinary(BinaryExpression binaryExpression)
at Microsoft.EntityFrameworkCore.SqlServer.Query.Internal.SqlServerSqlTranslatingExpressionVisitor.VisitBinary(BinaryExpression binaryExpression)
at Microsoft.EntityFrameworkCore.Query.RelationalSqlTranslatingExpressionVisitor.TranslateInternal(Expression expression)
at Microsoft.EntityFrameworkCore.Query.RelationalSqlTranslatingExpressionVisitor.Translate(Expression expression)
at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.TranslateExpression(Expression expression)
at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.TranslateLambdaExpression(ShapedQueryExpression shapedQueryExpression, LambdaExpression lambdaExpression)
at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.TranslateWhere(ShapedQueryExpression source, LambdaExpression predicate)
at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
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.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 Meerkat.Application.Persistence.GenericRepository`1.GetPageAsync(Int32 pageNumber, Int32 pageSize, Expression`1 filter, Func`2 orderBy, String includeProperties) in C:\Azure DevOps\Meerkat Back-end\Meerkat.Service\Meerkat.Application\Persistence\GenericRepository.cs:line 100
at Meerkat.Application.Facades.RealEstateFacade.GetPropertiesAsync(Int32 pageNumber, Int32 pageSize, SearchFilterModel filterModel, String includeProperties) in C:\Azure DevOps\Meerkat Back-end\Meerkat.Service\Meerkat.Application\Facades\RealEstateFacade.cs:line 126
at Meerkat.Application.Facades.RealEstateFacade.GetPropertiesAsync(SearchFilterModel filterModel, String includeProperties, Int32 pageNumber, Int32 propertiesPerPage) in C:\Azure DevOps\Meerkat Back-end\Meerkat.Service\Meerkat.Application\Facades\RealEstateFacade.cs:line 41
at Meerkat.WebService.Controllers.RealEstateController.GetProperties(String filter) in C:\Azure DevOps\Meerkat Back-end\Meerkat.Service\Meerkat.WebService\Controllers\RealEstateController.cs:line 34
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Logged|12_1(ControllerActionInvoker invoker)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Localization.RequestLocalizationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
我错过了什么?
感谢您的帮助。
LINQ Expression
s 是对象树,而不是要编译的文本集合。虽然来自源函数表达式的参数在外部看起来可能相同,但实际上它们是不同的对象,只是恰好具有相同的属性。因此,当您组合两个函数表达式并从其中一个中抛出参数时,您会得到一个 Expression
,它不包含所有信息。
为了使这一点更明显,假设您要将 a => a.Name == "test"
添加到 b => b.Age > 0
。您的代码将生成等同于 a => a.Name == "test" && b.Age > 0
... 的 LINQ 表达式,这会在混合中留下一个未知对象 b
。即使您更改了源表达式中的名称,它仍然是一个未知对象。
幸运的是,我们可以使用 ExpressionVisitor
来解决这个问题。这是我在类似情况下使用的一个:
class ExpressionReplacer : ExpressionVisitor
{
private readonly Expression From;
private readonly Expression To;
private ExpressionReplacer(Expression from, Expression to)
{
From = from;
To = to;
}
public override Expression Visit(Expression node)
{
if (ReferenceEquals(node, From))
return To;
return base.Visit(node);
}
public static T Replace<T>(T target, Expression from, Expression to)
where T : Expression
{
var replacer = new ExpressionReplacer(from, to);
return (T)replacer.Visit(target);
}
}
您可以在扩展方法中使用它来更改正在组合的函数之一的主体,以使用正确的参数实例,如下所示:
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> left, Expression<Func<T, bool>> right)
{
if (left == null) return right;
var right_body = ExpressionReplacer.Replace(right.Body, right.Parameters[0], left.Parameters[0]);
var and = Expression.AndAlso(left.Body, right_body);
return Expression.Lambda<Func<T, bool>>(and, left.Parameters[0]);
}
LINQ 表达式有趣又有趣,你可以用它们做一些非常有用的事情,表达式访问者都是乐趣的一部分。
我正在尝试以这种方式逐步构建 lambda 表达式:
public class PropertySearchFilter
{
public virtual Expression<Func<T,bool>> GetSearchFilter<T>(SearchFilterModel filterModelModel) where T: Property
{
Expression<Func<T, bool>> combinedFilter = null;
Expression<Func<T, bool>>? countryFilter = filterModelModel.CountryId.HasValue ? x => x.CountryId == filterModelModel.CountryId.GetValueOrDefault() : null;
Expression<Func<T, bool>>? cityFilter = filterModelModel.CityId.HasValue ? x => x.CityId == filterModelModel.CityId.GetValueOrDefault() : null;
Expression<Func<T, bool>>? categoryFilter = filterModelModel.CategoryId.HasValue ? x => x.CategoryId == filterModelModel.CategoryId.GetValueOrDefault() : null;
Expression<Func<T, bool>>? transactionTypeFilter = filterModelModel.TransactionTypeId.HasValue
? x => x.TransactionTypeId == filterModelModel.TransactionTypeId.GetValueOrDefault()
: null;
Expression<Func<T, bool>>? publicFilter = filterModelModel.IsPublic.HasValue ? x => x.IsPublic == filterModelModel.IsPublic.GetValueOrDefault() : null;
Expression<Func<T, bool>>? createdByFilter = !string.IsNullOrEmpty(filterModelModel.CreatedBy) ? x => x.CreatedBy == filterModelModel.CreatedBy : null;
Expression<Func<T, bool>>? minimumPriceFilter =
filterModelModel.MinimumPrice.HasValue ? x => x.Price >= filterModelModel.MinimumPrice.GetValueOrDefault() : null;
Expression<Func<T, bool>>? maximumPriceFilter = filterModelModel.MaximumPrice.HasValue ? x => x.Price <= filterModelModel.MaximumPrice.GetValueOrDefault() : null;
if (countryFilter != null)
combinedFilter = countryFilter;
if (cityFilter != null)
{
combinedFilter = combinedFilter.And(cityFilter);
}
if (categoryFilter != null)
{
combinedFilter = combinedFilter.And(categoryFilter);
}
if (transactionTypeFilter != null)
{
combinedFilter = combinedFilter.And(transactionTypeFilter);
}
if (publicFilter != null)
{
combinedFilter = combinedFilter.And(publicFilter);
}
if (createdByFilter != null)
{
combinedFilter = combinedFilter.And(createdByFilter);
}
if (minimumPriceFilter != null)
{
combinedFilter = combinedFilter.And(minimumPriceFilter);
}
if (maximumPriceFilter != null)
{
combinedFilter = combinedFilter.And(maximumPriceFilter);
}
return combinedFilter;
}
}
这是扩展方法:
public static class ExpressionExtensionsMethods
{
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> left, Expression<Func<T, bool>> right)
{
if (left == null) return right;
var and = Expression.AndAlso(left.Body, right.Body);
return Expression.Lambda<Func<T, bool>>(and, left.Parameters.Single());
}
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> left, Expression<Func<T, bool>> right)
{
if (left == null) return right;
var and = Expression.OrElse(left.Body, right.Body);
return Expression.Lambda<Func<T, bool>>(and, left.Parameters.Single());
}
}
我收到以下错误:
System.InvalidOperationException: The LINQ expression 'x' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to 'AsEnumerable', 'AsAsyncEnumerable', 'ToList', or 'ToListAsync'. See https://go.microsoft.com/fwlink/?linkid=2101038 for more information.
at Microsoft.EntityFrameworkCore.Query.RelationalSqlTranslatingExpressionVisitor.VisitParameter(ParameterExpression parameterExpression)
at Microsoft.EntityFrameworkCore.Query.RelationalSqlTranslatingExpressionVisitor.VisitMember(MemberExpression memberExpression)
at Microsoft.EntityFrameworkCore.Query.RelationalSqlTranslatingExpressionVisitor.VisitBinary(BinaryExpression binaryExpression)
at Microsoft.EntityFrameworkCore.SqlServer.Query.Internal.SqlServerSqlTranslatingExpressionVisitor.VisitBinary(BinaryExpression binaryExpression)
at Microsoft.EntityFrameworkCore.Query.RelationalSqlTranslatingExpressionVisitor.VisitBinary(BinaryExpression binaryExpression)
at Microsoft.EntityFrameworkCore.SqlServer.Query.Internal.SqlServerSqlTranslatingExpressionVisitor.VisitBinary(BinaryExpression binaryExpression)
at Microsoft.EntityFrameworkCore.Query.RelationalSqlTranslatingExpressionVisitor.TranslateInternal(Expression expression)
at Microsoft.EntityFrameworkCore.Query.RelationalSqlTranslatingExpressionVisitor.Translate(Expression expression)
at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.TranslateExpression(Expression expression)
at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.TranslateLambdaExpression(ShapedQueryExpression shapedQueryExpression, LambdaExpression lambdaExpression)
at Microsoft.EntityFrameworkCore.Query.RelationalQueryableMethodTranslatingExpressionVisitor.TranslateWhere(ShapedQueryExpression source, LambdaExpression predicate)
at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
at Microsoft.EntityFrameworkCore.Query.QueryableMethodTranslatingExpressionVisitor.VisitMethodCall(MethodCallExpression methodCallExpression)
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.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 Meerkat.Application.Persistence.GenericRepository`1.GetPageAsync(Int32 pageNumber, Int32 pageSize, Expression`1 filter, Func`2 orderBy, String includeProperties) in C:\Azure DevOps\Meerkat Back-end\Meerkat.Service\Meerkat.Application\Persistence\GenericRepository.cs:line 100
at Meerkat.Application.Facades.RealEstateFacade.GetPropertiesAsync(Int32 pageNumber, Int32 pageSize, SearchFilterModel filterModel, String includeProperties) in C:\Azure DevOps\Meerkat Back-end\Meerkat.Service\Meerkat.Application\Facades\RealEstateFacade.cs:line 126
at Meerkat.Application.Facades.RealEstateFacade.GetPropertiesAsync(SearchFilterModel filterModel, String includeProperties, Int32 pageNumber, Int32 propertiesPerPage) in C:\Azure DevOps\Meerkat Back-end\Meerkat.Service\Meerkat.Application\Facades\RealEstateFacade.cs:line 41
at Meerkat.WebService.Controllers.RealEstateController.GetProperties(String filter) in C:\Azure DevOps\Meerkat Back-end\Meerkat.Service\Meerkat.WebService\Controllers\RealEstateController.cs:line 34
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeActionMethodAsync>g__Logged|12_1(ControllerActionInvoker invoker)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeNextActionFilterAsync>g__Awaited|10_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Rethrow(ActionExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.<InvokeInnerFilterAsync>g__Awaited|13_0(ControllerActionInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|6_0(Endpoint endpoint, Task requestTask, ILogger logger)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Localization.RequestLocalizationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
我错过了什么?
感谢您的帮助。
LINQ Expression
s 是对象树,而不是要编译的文本集合。虽然来自源函数表达式的参数在外部看起来可能相同,但实际上它们是不同的对象,只是恰好具有相同的属性。因此,当您组合两个函数表达式并从其中一个中抛出参数时,您会得到一个 Expression
,它不包含所有信息。
为了使这一点更明显,假设您要将 a => a.Name == "test"
添加到 b => b.Age > 0
。您的代码将生成等同于 a => a.Name == "test" && b.Age > 0
... 的 LINQ 表达式,这会在混合中留下一个未知对象 b
。即使您更改了源表达式中的名称,它仍然是一个未知对象。
幸运的是,我们可以使用 ExpressionVisitor
来解决这个问题。这是我在类似情况下使用的一个:
class ExpressionReplacer : ExpressionVisitor
{
private readonly Expression From;
private readonly Expression To;
private ExpressionReplacer(Expression from, Expression to)
{
From = from;
To = to;
}
public override Expression Visit(Expression node)
{
if (ReferenceEquals(node, From))
return To;
return base.Visit(node);
}
public static T Replace<T>(T target, Expression from, Expression to)
where T : Expression
{
var replacer = new ExpressionReplacer(from, to);
return (T)replacer.Visit(target);
}
}
您可以在扩展方法中使用它来更改正在组合的函数之一的主体,以使用正确的参数实例,如下所示:
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> left, Expression<Func<T, bool>> right)
{
if (left == null) return right;
var right_body = ExpressionReplacer.Replace(right.Body, right.Parameters[0], left.Parameters[0]);
var and = Expression.AndAlso(left.Body, right_body);
return Expression.Lambda<Func<T, bool>>(and, left.Parameters[0]);
}
LINQ 表达式有趣又有趣,你可以用它们做一些非常有用的事情,表达式访问者都是乐趣的一部分。