c# - C# 将使用动态 linq 的 IEnumerable 函数转换为 IQueryable 函数
问题描述
我有这个静态函数,它根据一个或多个过滤器项返回过滤后的项目列表。它可以工作,但它设置为返回一个IEnumerable
. 关于这一点的事情是,我仍然想在编译表达式集并将其作为一个整体执行之前添加到表达式树中。
这是我当前从Pasov修改的过滤器代码。
namespace JobsLedger.API.ControllerServices.Shared.OrderAndFIlterHelpers {
public class ExpressionFilter {
public string PropertyName { get; set; }
public object Value { get; set; }
public Comparison Comparison { get; set; }
}
public enum Comparison {
Equal,
LessThan,
LessThanOrEqual,
GreaterThan,
GreaterThanOrEqual,
NotEqual,
Contains, //for strings
StartsWith, //for strings
IndexOf, //for strings
EndsWith, //for strings
BoolTest, //for bools
}
public static class DynamicFilteringHelper {
public static IEnumerable<T> ConstructAndExpressionTree<T>(IQueryable<T> source, List<ExpressionFilter> filters) {
if (filters.Count == 0)
return null;
ParameterExpression param = Expression.Parameter(typeof(T), "t");
Expression exp = null;
if (filters.Count == 1) {
exp = ExpressionRetriever.GetExpression<T>(param, filters[0]);
}
else {
exp = ExpressionRetriever.GetExpression<T>(param, filters[0]);
for (int i = 1; i < filters.Count; i++) {
exp = Expression.And(exp, ExpressionRetriever.GetExpression<T>(param, filters[i]));
}
}
var test = source.Where(Expression.Lambda<Func<T, bool>>(exp, param).Compile());
return test;
}
public static class ExpressionRetriever {
private static MethodInfo containsMethod = typeof(string).GetMethod("Contains", new Type[] { typeof(string), typeof(StringComparison) });
private static MethodInfo startsWithMethod = typeof(string).GetMethod("StartsWith", new Type[] { typeof(string), typeof(StringComparison) });
private static MethodInfo endsWithMethod = typeof(string).GetMethod("EndsWith", new Type[] { typeof(string), typeof(StringComparison) });
private static MethodInfo booleanEqualMethod = typeof(bool).GetMethod("Equals", new Type[] { typeof(bool) });
public static Expression GetExpression<T>(ParameterExpression param, ExpressionFilter filter) {
MemberExpression member = Expression.Property(param, filter.PropertyName);
ConstantExpression constant = Expression.Constant(filter.Value);
ConstantExpression comparisonType = Expression.Constant(StringComparison.OrdinalIgnoreCase);
switch (filter.Comparison) {
case Comparison.Equal:
return Expression.Equal(member, constant);
case Comparison.GreaterThan:
return Expression.GreaterThan(member, constant);
case Comparison.GreaterThanOrEqual:
return Expression.GreaterThanOrEqual(member, constant);
case Comparison.LessThan:
return Expression.LessThan(member, constant);
case Comparison.LessThanOrEqual:
return Expression.LessThanOrEqual(member, constant);
case Comparison.NotEqual:
return Expression.NotEqual(member, constant);
case Comparison.Contains:
return Expression.Call(member, containsMethod, constant, comparisonType);
case Comparison.StartsWith:
return Expression.Call(member, startsWithMethod, constant, comparisonType);
case Comparison.IndexOf:
return Expression.NotEqual(
Expression.Call(
member,
"IndexOf",
null,
Expression.Constant(filter.Value, typeof(string)),
Expression.Constant(StringComparison.InvariantCultureIgnoreCase, typeof(StringComparison))
),
Expression.Constant(-1, typeof(int))
);
case Comparison.EndsWith:
return Expression.Call(member, endsWithMethod, constant);
case Comparison.BoolTest:
return Expression.Call(member, booleanEqualMethod, constant);
default:
return null;
}
}
}
}
}
在 DynamicFilteringHelper 方法的末尾,它具有表达式:
var test = source.Where(Expression.Lambda<Func<T, bool>>(exp, param).Compile());
这会编译表达式并使用“where”将其应用于源。
相反,我想生成一个表达式树,排序函数可以在编译之前添加到它。在这里,因为它返回一个IEnumerable
它已经被编译,我想在过滤和排序之后编译它。
所以我开始组合一个调用,它结合了所有过滤表达式并使用“where”将其应用于源。
var filteredExpressions = Expression.Lambda(exp, param);
MethodCallExpression call = Expression.Call(
typeof(Queryable),
"where",
new[] { typeof(T) },
source.Expression,
Expression.Quote(filteredExpressions)
);
return source.Provider.CreateQuery<T>(call);
这不起作用,我得到"Operation is not valid due to the current state of the object."
有没有办法返回 expressonTree 以便在我编译和检索结果之前添加它?或者我可以编译它然后将它用于排序功能..
我的印象是最好在执行之前完整地创建表达式树,这就是为什么我想返回一个 IQueryable 而不是 IEnumerable ...
关于排序方法的额外信息..
仅供参考,我接下来想调用这种排序静态排序函数..
public static class DynamicOrderingHelper {
public static IOrderedQueryable<T> OrderByAndThenBy<T>(IQueryable<T> source, List<KeyValuePair<string, SortDirection>> sortItems) {
IOrderedQueryable<T> sortedSource = null;
for (int i = 0; i < sortItems.Count; i++) {
if (i == 0) {
sortedSource = sortItems[0].Value == SortDirection.asc ? OrderingHelper(source, sortItems[0].Key, false, false) : OrderingHelper(source, sortItems[0].Key, true, false);
}
else {
sortedSource = sortItems[i].Value == SortDirection.asc ? OrderingHelper(sortedSource, sortItems[i].Key, false, true) : OrderingHelper(sortedSource, sortItems[i].Key, true, true);
}
}
return sortedSource;
}
private static IOrderedQueryable<T> OrderingHelper<T>(IQueryable<T> source, string propertyName, bool descending, bool anotherLevel) {
ParameterExpression param = Expression.Parameter(typeof(T), string.Empty); // I don't care about some naming
MemberExpression property = Expression.PropertyOrField(param, propertyName);
LambdaExpression sort = Expression.Lambda(property, param);
MethodCallExpression call = Expression.Call(
typeof(Queryable),
(!anotherLevel ? "OrderBy" : "ThenBy") + (descending ? "Descending" : string.Empty),
new[] { typeof(T), property.Type},
source.Expression,
Expression.Quote(sort));
return (IOrderedQueryable<T>)source.Provider.CreateQuery<T>(call);
}
编辑
正如 Ivan 建议的那样,我这样做了,返回类型返回“由于对象的当前状态而导致操作无效”。我认为这意味着它需要编译,但它不会接受在 IQueryable 和 .ToList() 上也不起作用..
顺便说一句,这发生在下面两行中的第一行.. source 有 21 条记录,但返回该错误:
var test = source.Where(Expression.Lambda<Func<T, bool>>(exp, param));
return test;