在提供的`Expression`之上添加`MethodCallExpression`

时间:2017-10-14 17:00:21

标签: c# .net linq reflection expression

我的表达树起源于Linq,例如Trying ::1... TCP_NODELAY set Connected to localhost (::1) port 8080 (#0) GET /mydataset/page/def/sector-publico/contrato/cod-centro-destino HTTP/1.1 Host: localhost:8080 User-Agent: curl/7.53.1 Accept: application/rdf+xml HTTP/1.1 200 Cache-Control: no-cache Pragma: no-cache Content-Type: text/html;charset=utf-8 Content-Length: 4341 Date: Sat, 14 Oct 2017 16:55:21 GMT 。表达式如下:

leCollection.Where(...).OrderBy(...).Skip(n).Take(m)

现在,这是我理想的状态,我有Take(Skip(OrderBy(Where(...), ...), n), m) // you got the idea Take,但这不是规则。如果需要,我想以编程方式添加Skip / Take

我想出了如何更改Skip / Take参数,如果我检测到它,我甚至可以在Skip下添加Skip&# 39;不存在,但我正在努力弄清楚如何在表达的顶部添加Take - 我不知道如何识别我实际上正在访问顶级表达。我编写的方法是在树中的每个方法调用上执行的,所以在我对表达式执行任何操作之前,我必须检查方法名称。

以下是我用于更改Take / Take并在Skip下添加Skip的方法。这些工作,我现在也有兴趣将Take置于树顶,如果它还没有出现的话。谁能指引我到任何智慧的地方,在那里我可以学到更多东西?

Take

1 个答案:

答案 0 :(得分:1)

您可以覆盖Visit方法并使用flag变量来检查这是否是第一次调用它 下一个代码会检查一个top方法,如果它不是Take添加对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));
    }
}

我用这段代码测试了它:

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)