本文介绍了在提供的“表达式"顶部添加"MethodCallExpression"的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个源于Linq的表达式树,例如leCollection.Where(...).OrderBy(...).Skip(n).Take(m).表情看起来像:

I'm having expression tree originating in Linq, e.g. leCollection.Where(...).OrderBy(...).Skip(n).Take(m). Expression looks like:

Take(Skip(OrderBy(Where(...), ...), n), m) // you got the idea

现在,这是我的理想状态,我在那里有TakeSkip,但这不是规则.如果需要,我想以编程方式添加Take/Skip.

Now, this is my ideal state that I have Take and Skip there, but it is not the rule. I would like to add Take/Skip programmatically if needed.

我想出了一种方法来更改Take/Skip参数,如果我发现Take/Skip不存在,甚至可以在Take下添加Skip,但是我一直在努力寻找答案如何在表达式的顶部添加Take-我不知道该如何识别我实际上在访问顶部的表达式.我编写的方法是在树中的每个方法调用上执行的,因此在对表达式执行任何操作之前,我必须检查方法名称.

I came up with way how to change Take/Skip argument, and I'm even able to add Skip under Take if I detect it's not present, but I'm struggling to figure out how to add Take at the top of expression - I don't know how to recognize I'm actually visiting top expression. Methods I wrote are executed on every method call in tree, so I had to check method name before I do anything with expression.

以下是我用于更改Take/Skip并在Take下添加Skip的方法.这些工作,我现在也有兴趣将Take放置在树的顶部(如果尚不存在).任何人都可以将我带到任何可以学习更多知识的智慧地方吗?

Here are methods I'm using for altering Take/Skip and adding Skip under Take. Those work, I'm now also interested in placing Take on top of tree if it's not yet present. Could anyone direct me to any place of wisdom, where I can learn more?

public class LeVisitor<TEntity> : ExpressionVisitor
    where TEntity : class
{
    private readonly int? _take;
    private readonly int? _skip;
    private readonly MethodInfo _queryableSkip;

    public LeVisitor(int? take, int? skip)
    {
        // ...
    }

    protected override Expression VisitMethodCall(MethodCallExpression node)
    {
        return base.VisitMethodCall(AlterTake(AlterSkip(node)));
    }

    private MethodCallExpression AlterTake(MethodCallExpression node)
    {
        if (!_take.HasValue || !node.Method.Name.Equals("Take", StringComparison.Ordinal))
        {
            return node;
        }

        Expression innerCall = node.Arguments[0];
        if (_skip != null)
        {
            var innerMethod = innerCall as MethodCallExpression;
            if (innerMethod != null && !innerMethod.Method.Name.Equals("Skip", StringComparison.Ordinal))
            {
                ConstantExpression skipConstant = Expression.Constant(_skip, typeof(int));
                innerCall = Expression.Call(_queryableSkip, new[] { innerCall, skipConstant });
            }
        }

        return node.Update(
            node.Object,
            new[]
            {
                innerCall,
                Expression.Constant(_take, typeof(int))
            });
    }

    private MethodCallExpression AlterSkip(MethodCallExpression node)
    {
        if (!_skip.HasValue || !node.Method.Name.Equals("Skip", StringComparison.Ordinal))
        {
            return node;
        }

        return node.Update(
            node.Object,
            new[]
            {
                node.Arguments[0],
                Expression.Constant(_skip, typeof(int))
            });
    }
}

推荐答案

您可以覆盖Visit方法,并使用flag变量检查这是否是对其的第一次调用.
接下来的代码将检查top方法,如果不是Take add调用Queryable.Take

You can override Visit method and use flag variable to check if this is a very first call to it.
Next code will check a top method and if it's not a Take add call to Queryable.Take

public class AddTakeVisitor : ExpressionVisitor
{
    private readonly int takeAmount;
    private bool firstEntry = true;

    public AddTakeVisitor(int takeAmount)
    {
        this.takeAmount = takeAmount;
    }

    public override Expression Visit(Expression node)
    {
        if (!firstEntry)
            return base.Visit(node);

        firstEntry = false;
        var methodCallExpression = node as MethodCallExpression;
        if (methodCallExpression == null)
            return base.Visit(node);

        if (methodCallExpression.Method.Name == "Take")
            return base.Visit(node);

        var elementType = node.Type.GetGenericArguments();
        var methodInfo = typeof(Queryable)
            .GetMethod("Take", BindingFlags.Public | BindingFlags.Static)
            .MakeGenericMethod(elementType.First());
        return Expression.Call(methodInfo, node, Expression.Constant(takeAmount));
    }
}

我已经使用以下代码对其进行了测试:

I've tested it with this code:

var exp = (new[] {1, 2, 3}).AsQueryable().Skip(1);
var visitor = new AddTakeVisitor(1);
var modified = visitor.Visit(exp.Expression);

modified.DebugView看起来像这样:

.Call System.Linq.Queryable.Take(
    .Call System.Linq.Queryable.Skip(
        .Constant<System.Linq.EnumerableQuery`1[System.Int32]>(System.Int32[]),
        1),
    1)

这篇关于在提供的“表达式"顶部添加"MethodCallExpression"的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-30 22:03