首页 > 解决方案 > 如何获取 IQueryable 中使用的方法和值?

问题描述

我正在尝试解析一个IQueryable并且我想获取在IQueryable.

例如

var query = Queryable.Skip(10).Take(100);

bool hasCalledSkipMethod = HasSkipMethod(query);
var skipValue = GetSkipMethodParameterValueSomeHow(query);

bool hasCalledTakeMethod = HasTakeMethod(query);
var takeValue = GetTakeMethodParameterValueSomeHow(query);

或者一些通用方法接收IQueryable并返回所有方法名称和参数。

我怎样才能得到方法和它在一个中应用的参数IQueryable

标签: c#iqueryable

解决方案


不确定这是否是最好的方法,但它可能适用于您想要的,或者至少可以引导您朝着正确的方向前进。

每个节点IQueryable都有一个Expression表示要作为树数据结构执行的查询,其中每个节点本身也是一个Expression. 因此,每当您.Take(int)IQueryable幕后发生的事情时,都会MethodCallExpression将该方法的新类型节点Take(int)添加到该的表达式树中IQueryable

文档

Take(IQueryable, Int32) 方法生成一个 MethodCallExpression,它表示调用 Take(IQueryable, Int32) 本身作为构造的泛型方法。然后它将 MethodCallExpression 传递给由源参数的 Provider 属性表示的 IQueryProvider 的 CreateQuery(Expression) 方法。

因此,您要做的是遍历(访问树中的每个节点)并查看是否存在MethodCallExpression表达式的方法名称所在的类型的节点,在本例中为"Take".

要遍历表达式树,您可以创建一个ExpressionVisitor覆盖方法VisitMethodCall(MethodCallExpression)。此自定义访问者可以接受要检查的方法名称作为构造函数参数,并且在访问节点时,如果找到方法名称,则将其存储在内部属性中。

一旦有了自定义访问者,就可以调用Visit(Expression)where 表达式 will be queryable.Expression

这是它的要点。

using System.Linq;
using System.Linq.Expressions;
                    
public class Program
{
    public static void Main()
    {       
        IQueryable<int> queryable = new []{ 78, 92, 100, 37, 81 }
            .AsQueryable()
            .Skip(1)
            .Take(2);

        Expression expression = queryable.Expression;
        
        var hasTakeMethodVisitor = new HasMethodVisitor("Take");
        var hasSkipMethodVisitor = new HasMethodVisitor("Skip");
        var hasWhereMethodVisitor = new HasMethodVisitor("Where");
        
        hasTakeMethodVisitor.Visit(expression);
        hasSkipMethodVisitor.Visit(expression);
        hasWhereMethodVisitor.Visit(expression);
        
        System.Console.WriteLine("Has Take Method? {0}", hasTakeMethodVisitor.HasMethod);
        System.Console.WriteLine("Has Skip Method? {0}", hasSkipMethodVisitor.HasMethod);
        System.Console.WriteLine("Has Where Method? {0}", hasWhereMethodVisitor.HasMethod);
    }
    
    internal class HasMethodVisitor : ExpressionVisitor {
        
        private readonly string _methodToFind;
        
        public HasMethodVisitor(string methodName) {
            _methodToFind = methodName;
        }
        
        public bool HasMethod { get; private set; }
        
        protected override Expression VisitMethodCall(MethodCallExpression node) {
            HasMethod |= node.Method.Name == _methodToFind;
        
            return base.VisitMethodCall(node);
        }
    }
}

输出

Has Take Method? True
Has Skip Method? True
Has Where Method? False

推荐阅读