0

T 是一种可能有也可能没有特定属性的类型,比如说“City”。对于具有名为“City”的属性的类型,我想限制记录,以便仅返回 Gotham 的居民并对其进行排序。

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);
}

如果可能的话,请在此处命名/链接一些有用的文献,以防我必须创建更复杂的查询,即混合和/或

该代码是从How do I apply OrderBy on an IQueryable using a string column name in a generic extension method?借来的 ?

4

3 回答 3

2

我不太确定我是否正确理解了你,但我想几个月前我也遇到过同样的情况。这里发布的代码是我的解决方案。

我想你应该对第 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());
于 2013-01-24T16:10:37.817 回答
1

我认为你让这件事变得比你必须做的更难。在代码的第一部分(OrderBy())中,您实际上不需要生成整个查询表达式,只需要生成其中的 lambda。在第二部分(可选Where())中,您可以做几乎相同的事情,只需添加Expression.Equal()and 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()

于 2013-01-24T19:05:08.740 回答
0

你已经成功了一半。您已经有了有序的表达式,所以您只需在Queryable.OrderBy表达式中使用表达式调用Queryable.Where(反之亦然,并不重要)。

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));
}

有一段时间没有手动构建表达式了,所以这纯粹是凭记忆完成的。但是,它至少应该让您开始并为您提供一些可以使用的东西。

PS我实际上会在OrderBy方法调用之前执行此检查。这样你最终会得到Queryably.Where(...).OrderBy(...)相反的结果。但我想如果这还是由提供商翻译的,那应该没关系。但是,我会做一些事情来减少生成的查询的任何歧义。

于 2013-01-24T18:35:59.223 回答