Основная проблема не в классе, а в том, как вы его используете:
var whereDeleg = QueryExpressionBuilder.GetExpression<Tax>(filters).Compile();
var myList = _dbContext.MyEntity.Where(whereDeleg).ToList();
Вы берете Expression<Func<T, bool>>
из вашего метода, но затем вызов Complie()
преобразует его в Func<T, bool>
,Таким образом, хотя _dbContext.MyEntity
равно IQueryable<T>
, не существует IQueryable<T>
метода расширения Where
, принимающего Func<T, bool>
(все они принимают Expression<Func<T, bool>>
).Но поскольку IQueryable<T>
наследует (следовательно, является) IEnumerable<T>
, компилятор находит и использует метод расширения Where
для IEnumerable<T>
(определенный в классе Enumerable
).
Это заставляет Where
(и все последующие методы, если таковые имеются) выполнять на стороне клиента после выполнения и материализации запроса до Where
(в вашем случае - всей таблицы).
Разница между IQueryable<T>
и IEnumerable<T>
покрывается Возвращая IEnumerable против IQueryable .Все, что вам нужно, это убедиться, что вы всегда вызываете IQueryable<T>
методы расширения вместо IEnumerable<T>
методов с тем же именем и аналогично выглядящим аргументами, используя Expression<Func<...>>
вместо Func<...>
.
Учитывая все вышесказанное, вы должны использовать результат метода напрямую, не вызывая Compile
:
var predicate = QueryExpressionBuilder.GetExpression<Tax>(filters);
var myList = _dbContext.MyEntity.Where(predicate).ToList();
или просто
var myList = _dbContext.MyEntity.Where(QueryExpressionBuilder.GetExpression<Tax>(filters)).ToList();
Или, что еще лучше, добавьте следующий пользовательскийметод расширения до QueryExpressionBuilder
класса:
public static IQueryable<T> Where<T>(this IQueryable<T> source, IList<Filter> filters)
{
var predicate = GetExpression<T>(filters);
return predicate != null ? source.Where(predicate) : source;
}
, чтобы можно было просто использовать (и минимизировать вероятность ошибок):
var myList = _dbContext.MyEntity.Where(filters).ToList();
Примечание:Реализация метода основного построителя выражений слишком сложна и также уничтожает переданный список ввода filters
.Это можно упростить следующим образом (который не имеет вышеупомянутого дефекта):
public static Expression<Func<T, bool>> GetExpression<T>(IEnumerable<Filter> filters)
{
var param = Expression.Parameter(typeof(T), "t");
var body = filters
.Select(filter => GetExpression(param, filter))
.DefaultIfEmpty()
.Aggregate(Expression.AndAlso);
return body != null ? Expression.Lambda<Func<T, bool>>(body, param) : null;
}