【问题标题】:Reusable LINQ method query可重用的 LINQ 方法查询
【发布时间】:2016-12-27 06:20:18
【问题描述】:

是否可以通过调用OrderByWhere 创建可重用的LINQ 查询表达式,而不将其应用于特定的IQueryable

我希望能够做这样的事情:

var data = new List<PlayerDTO>();
var builder = new Builder<PlayerDTO>();
var query = builder.GetQuery();
var results = data.AsQueryable().Provider.CreateQuery(query);

如果不将 IQueryable 对象提供给 GetQuery 方法,我将无法正常工作。

这是我目前的代码:

public Expression GetQuery()
{
    var type = typeof(T); // T is PlayerDTO
    var col = type.GetProperty(OrderBy.Column);

    var orderByMethod = typeof(Queryable).GetMethods().Single(
        method => method.Name == "OrderBy"
                  && method.IsGenericMethodDefinition
                  && method.GetGenericArguments().Length == 2
                  && method.GetParameters().Length == 2);

    var genericOrdebyMethod = orderByMethod.MakeGenericMethod(typeof(T), col.PropertyType);

    var parameter = Expression.Parameter(typeof(T), "p"); // {p}
    var property = Expression.Property(parameter, col); // {p.ID}
    var lambda = Expression.Lambda<Func<T, int>>(property, parameter); // {p => p.ID}

    //This list should not exist here
    var tempList = new List<PlayerDTO>() { new PlayerDTO(1, "First"), new PlayerDTO(2, "Second") };

    var orderByMethodExpression = Expression.Call(genericOrdebyMethod, tempList.AsQueryable().Expression, lambda); // {tempList.OrderBy(p => p.ID)}

    var results = tempList.AsQueryable().Provider.CreateQuery(orderByMethodExpression);

    return orderByMethodExpression;
}

相关部分是对Expression.Call 的调用,我必须在其中提供IQueryable 才能工作,但我希望能够在不指定现有IQueryable 的情况下构建表达式。

这可能吗?

编辑:
现在我考虑到这一点,实际上我根本不需要这样做......将 IQueryable 作为参数发送给 GetQuery 方法是非常有意义的。不过,我会继续提出这个问题。

【问题讨论】:

  • 你不能用Expression&lt;Func&lt;PlayerDTO,object&gt;&gt; = p =&gt; p.ColumnName;吗?您可以在 OrderBy 方法中使用该表达式。如果您的 PlayerDTO 实现了一些接口来了解 columnName 属性,这将起作用。否则,您仍然可以仅对属性使用反射。
  • 是的,我可以,但我的主要问题是我想包含在表达式中的 OrderBy/Where/etc 类型方法,然后在多个 IQueryables 上重用。
  • 只是你在做什么有点奇怪,动态构建一个表达式,使用Provider将它变成一个iQueryable,而你事先知道你将使用哪个属性和哪个方法。 IQueryable 上的 Linq 扩展方法实际上与您现在尝试手动执行的操作完全相同(请参阅 this link)。
  • 我不确定我是否完全理解.. 我要订购列表的属性仅在运行时才知道。然后基于该属性,我想用一些过滤器构建一个 OrderBy 表达式并从方法中返回它。我会用我正在尝试做的事情来更新我的问题!
  • 哦,在这种情况下,我会创建一个扩展方法,它接受IQueryable&lt;T&gt; 并返回IQueryable&lt;T&gt;,这样会更有意义。例如:var result = data.AsQueryable().ModifyQuery();

标签: c# linq expression-trees dynamic-linq


【解决方案1】:

您可以将其创建为从 IQueryable 到 IOrderedQueryable 的另一个表达式,类似于:

public Expression getOrderByQuery<T>(PropertyInfo col)
{
    var orderByMethod = typeof(Queryable).GetMethods().Single(
        method => method.Name == "OrderBy"
                  && method.IsGenericMethodDefinition
                  && method.GetGenericArguments().Length == 2
                  && method.GetParameters().Length == 2);

    var genericOrdebyMethod = 
                        orderByMethod.MakeGenericMethod(typeof(T), col.PropertyType);

    var parameter = Expression.Parameter(typeof(T), "p"); // {p}
    var property = Expression.Property(parameter, col); // {p.ID}
    var lambda = Expression.Lambda<Func<T, int>>(property, parameter); // {p => p.ID}

    var paramList = Expression.Parameter(typeof(IQueryable<T>));

    // {tempList.OrderBy(p => p.ID)}    
    var orderByMethodExpression = Expression.Call(genericOrdebyMethod, paramList, lambda); 

    return Expression.Lambda(orderByMethodExpression, paramList);
}

我是这样用的,我的清单会被排序:

var tempList = new List<PlayerDTO>() 
  { 
     new PlayerDTO(2, "First"), 
     new PlayerDTO(1, "Second") 
  };

var e = (Expression<Func<IQueryable<PlayerDTO>, IOrderedQueryable<PlayerDTO>>>)
               getOrderByQuery<PlayerDTO>(typeof(PlayerDTO).GetProperty("Id"));
e.Compile().Invoke(tempList.AsQueryable()).Dump();

【讨论】:

  • 感谢 Maksim,这行得通!我发现 Compile 的转换和调用真的很难看,有没有什么方法可以让它更干净或者这很简单?
  • @ShahinDohan 不,不幸的是我不知道,你可以在你的方法中进行转换,据我所知,应该在某个地方进行转换和编译。
  • 我决定根本不这样做,但如果其他人这样做,我会继续提出问题。无论如何谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-24
  • 2019-02-15
  • 2016-03-24
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多