>T 是一种可能具有也可能没有特定属性的类型,例如"City"。对于具有名为"City"的属性的类型,我想限制记录,以便仅返回哥谭的居民并对其进行排序。
public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string ordering, params object[] values)
{
var type = typeof(T);
var property = type.GetProperty(ordering);
var parameter = Expression.Parameter(type, "p");
var propertyAccess = Expression.MakeMemberAccess(parameter, property);
var orderByExp = Expression.Lambda(propertyAccess, parameter);
MethodCallExpression resultExp = Expression.Call(
typeof(Queryable),
"OrderBy",
new Type[] { type, property.PropertyType },
source.Expression,
Expression.Quote(orderByExp));
string propertyToRestrictOn = "City";
string restrictedValue = "Gotham";
var restrictedProperty = type.GetProperty(propertyToRestrictOn);
if(null ! = restrictedProperty )
{
// TODO: What to add here so than only those records are returned that have a
// property named City and the value is 'Gotham'???
}
return source.Provider.CreateQuery<T>(resultExp);
}
如果可能的话,请在这里命名/链接一些有用的文献,以防万一我必须创建更复杂的查询,即混合和/或
代码是从如何在通用扩展方法中使用字符串列名称在 IQueryable 上应用 OrderBy?
我不太确定我是否正确理解了你,但我想几个月前我也有同样的情况。这里发布的代码是我的解决方案。
我认为您应该对第 24 行特别感兴趣。
编辑:
PropertyInfo p = ... // I used reflection in my examply to get properties with a certain Attribute
var expressionParameter = Expression.Parameter(typeof(SomeClass), "lambda");
var parameter = new [] { expressionParameter };
var propertyAccess = Expression.Property(expressionParameter, p);
var nullCheck = Expression.NotEqual(propertyAccess, Expression.Constant(null, p.PropertyType));
var nullCheckLambda = Expression.Lambda<Func<SomeClass, Boolean>>(nullCheck, parameter);
var containsMethodInfo = typeof(String).GetMethod("Contains", new[] { typeof(String) });
var contains = Expression.Call(propertyAccess, containsMethodInfo, Expression.Constant("ell"));
var containsLambda = Expression.Lambda<Func<SomeClass, Boolean>>(contains, new[] { expressionParameter });
var predicate = Expression.Lambda<Func<SomeClass, Boolean>>(
// line 24
Expression.AndAlso(nullCheckLambda.Body, containsLambda.Body), parameter);
Console.WriteLine(predicate.ToString());
我认为你让这件事变得比你必须的更难。在代码的第一部分(OrderBy()
),您实际上不需要生成整个查询表达式,只需生成其中的 lambda。在第二部分(可选Where()
)中,您可以做几乎相同的事情,只需添加Expression.Equal()
和Expression.Constant()
:
public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string ordering)
{
var type = typeof(T);
var property = type.GetProperty(ordering);
var parameter = Expression.Parameter(type, "p");
var propertyAccess = Expression.MakeMemberAccess(parameter, property);
// necessary for value types to work
var cast = Expression.Convert(propertyAccess, typeof(object));
var orderByExp = Expression.Lambda<Func<T, object>>(cast, parameter);
IQueryable<T> result = source.OrderBy(orderByExp);
string propertyToRestrictOn = "City";
string restrictedValue = "Gotham";
var restrictedProperty = type.GetProperty(propertyToRestrictOn);
if (restrictedProperty != null)
{
var restrictionParameter = Expression.Parameter(type, "p");
var restrictionPropertyAccess =
Expression.MakeMemberAccess(restrictionParameter, restrictedProperty);
var restrictionEquality =
Expression.Equal(restrictionPropertyAccess,
Expression.Constant(restrictedValue));
var whereExp =
Expression.Lambda<Func<T, bool>>(restrictionEquality, restrictionParameter);
result = result.Where(whereExp);
}
return result;
}
另外,如果您的方法不仅仅是订购,我认为您不应该将其称为OrderBy()
。
你已经走到了一半。您已经有了有序表达式,因此您只需在Queryable.Where
表达式中使用Queryable.OrderBy
表达式调用(反之亦然,这并不重要)。
if(null != restrictedProperty )
{
var notEqualExp = Expression.NotEqual(parameter,
Expression.Constant(restrictedValue, typeof(string)));
resultExp = Expression.Call(
typeof(Queryable),
"Where",
new Type[] { type },
resultExp,
Expression.Lambda(notEqualExp, parameter));
}
有一段时间没有手动构建表达式了,所以这纯粹是根据记忆完成的。但是,它至少应该让您入门并为您提供一些工作。
附言我实际上会在OrderBy
方法调用之前执行此检查。这样你最终会得到Queryably.Where(...).OrderBy(...)
。但我想如果这无论如何都被提供商翻译,那么应该无关紧要。但是,我会做一些事情来减少生成的查询的任何歧义。