在任意IQueryable <t> </t>上获取Count()LINQ扩展方法的MethodInfo

时间:2012-04-23 08:25:16

标签: c# linq count iqueryable

我有IQueryable&lt; T&GT;源和我想动态调用IQueryable&lt; T&GT; .Count之间()

所以,我需要在IQueryable中声明的Count方法的MethodInfo。

这是来自msdn的签名(在IQueryable&lt;&gt;中):

public static int Count<TSource>(
    this IQueryable<TSource> source
)

这是我有多远:

Expression expr; //this is expression which holds my IQueryable<T>
MethodInfo mi = expr.Type.GetMethod("Count", BindingFlags.Static | BindingFlags.Public, null, new[] { expr.Type }, null); 

但我的mi总是空的;

我也尝试过:

mi = typeof(IQueryable<>).GetMethod("Count", BindingFlags.Static | BindingFlags.Public, null, new[] { expr.Type }, null);

但又是null。

我的最终目标是:

Expression.Call(mi, expr);

更新: 这就是我得到Sum Extension方法的方法:

MethodInfo sum = typeof(Queryable).GetMethod("Sum", BindingFlags.Static | BindingFlags.Public, null, new[] { typeof(IQueryable<decimal>) }, null);

这是有效的,但这个Sum方法不是通用的。它虽然是静态的。

2 个答案:

答案 0 :(得分:9)

你需要破解IQueryable<T>类型的泛型参数并使用它;同样拥有该方法的类型不是IQueryable<T>,它是Queryable - 如果你考虑它 - 接口不能有静态方法(好吧,正如评论者指出的那样,在C#中是):)。

此外,因为它是一种通用方法,您无法以您尝试的方式匹配参数:因为您需要传递泛型类型定义 IQuerable<TSource> - 通用类型 IQueryable<int>或实际表达式。

相反,您只需在Queryable类型上查找名为“Count”的静态方法的单参数版本:

Type genericArgument = expr.GetGenericArguments()[0];

MethodInfo countMethod = typeof(Queryable)
              .GetMethods(BindingFlags.Static | BindingFlags.Public)
              //narrow the search before doing 'Single()'
              .Single(mi => mi.Name == "Count"
                         // this check technically not required, but more future proof
                         && mi.IsGenericMethodDefinition
                         && mi.GetParameters().Length == 1)
              .MakeGenericMethod(genericArgument);

//now you can bind to 'countMethod'

2017年3月7日更新 - 很明显,框架中发生了一些变化,导致代码示例的原始版本停止工作 - 这是一个更新版本应该工作

进一步说明 - 方法的签名是:

public static int Count<TSource>(
  this IQueryable<TSource> source
)

因此,虽然参数类型为IQueryable<TSource>,但它在TSource类型上是通用的 - 因此您需要捕获IQueryable<TSource>表达式并抓住它的泛型参数。你也应该能够在这里看到我对这个参数的意思。

答案 1 :(得分:3)

让编译器为您获取方法。

Type genericArgument = expr.GetGenericArguments()[0];
var fakeExp = (Expression<Func<IQueryable<int>, int>>)(q => q.Count());
var mi = ((MethodCallExpression)fakeExp.Body).Method.GetGenericMethodDefinition().MakeGenericMethod(genericArgument);