问题描述
我有一个源于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
现在,这是我的理想状态,我在那里有Take
和Skip
,但这不是规则.如果需要,我想以编程方式添加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"的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!