如何获取 IQueryable 中使用的方法和值?

How to get methods and values used in an 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 和 returns 所有方法名称和参数的通用方法。

如何获取在 IQueryable 中应用的方法及其参数?

不确定这是否是最佳方法,但它可能会满足您的需求,或者至少可以指导您朝着正确的方向前进。

每个 IQueryable 都有一个 Expression that represents the query to be executed as a tree data structure where each node is in itself also an Expression. So whenever you do .Take(int) in a IQueryable what's happening behind the scenes is that a new Node of type MethodCallExpression 方法 Take(int) 被添加到那个 IQueryable 的表达式树中。

来自docs

The Take(IQueryable, Int32) method generates a MethodCallExpression that represents calling Take(IQueryable, Int32) itself as a constructed generic method. It then passes the MethodCallExpression to the CreateQuery(Expression) method of the IQueryProvider represented by the Provider property of the source parameter.

因此,你要做的是遍历(访问树中的每个节点),看看是否有MethodCallExpression类型的节点,其中方法在这种情况下,表达式的名称是 "Take".

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

有了自定义访客后,您可以调用 Visit(Expression) where expression 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