我正在嘗試以這種方式逐步構建 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)
我錯過了什么?
謝謝你的幫助。
uj5u.com熱心網友回復:
LINQExpression是物件樹,而不是要編譯的文本集合。雖然來自源函式運算式的引數在外部看起來可能相同,但它們實際上是恰好具有相同屬性的不同物件。因此,當您組合兩個函式運算式并從其中一個中拋出引數時,您將得到一個Expression不包含所有資訊的引數。
為了使這一點更明顯,假設您正在a => a.Name == "test"添加b => b.Age > 0. 您的代碼將生成一個等效于a => a.Name == "test" && b.Age > 0... 的 LINQ 運算式,這會在混合中留下一個未知物件b。即使您更改了源運算式中的名稱,它仍然是一個未知物件。
幸運的是,我們可以使用 anExpressionVisitor來為我們解決這個問題。這是我在類似情況下使用過的一個:
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 運算式很有趣,你可以用它們做一些非常有用的事情,運算式訪問者都是樂趣的一部分。
轉載請註明出處,本文鏈接:https://www.uj5u.com/qianduan/457090.html
標籤:C# asp.net-mvc asp.net 核心
