我知道从MSDN的文章有关如何修改表达式树什么的ExpressionVisitor是应该做的.它应该修改表达式.
然而,他们的例子非常不现实,所以我想知道为什么我需要它?你能说出一些真实世界的案例吗?修改表达式树会有意义吗?或者,为什么必须进行修改?从什么到什么?
它还有许多重载用于访问各种表达式.我如何知道何时应该使用它们以及它们应该返回什么?另一方面,我看到人们使用VisitParameter并返回base.VisitParameter(node)另一方正在返回Expression.Parameter(..).
下面是我的问题的简单演示代码.
[TestClass]
public class ExpressionTests
{
[TestMethod]
public void TestParam()
{
Search<Student>(s => s.Id == 1L);
GetStudent(1L);
}
private void GetStudent(long id)
{
Search<Student>(s => s.Id == id);
}
private void Search<T>(Expression<Func<T, bool>> filter)
{
var visitor = new MyExpressionVisitor();
visitor.Visit(filter);
}
}
public class MyExpressionVisitor : ExpressionVisitor
{
protected override Expression VisitConstant(ConstantExpression node)
{
Assert.AreEqual(1L, node.Value);
return base.VisitConstant(node);
}
}
Run Code Online (Sandbox Code Playgroud)
TestParam方法导致VisitConstant在两个不同的路径上调用:
1. TestParam - > Search- >VisitConstant
在此执行路径中,传递给Search方法的常量表达式(1L)是实常数值.在这里,一切都很好,断言按预期成功.当VisitConstant通过第一路径调用node.Value.GetType()是 …
给出了Expression<Func<TEntity, bool>>一条线
entity => entity.SubEntity.Any(
subEntity => (
(subEntity.SomeProperty == False)
AndAlso
subEntity.SubSubEntity.FooProperty.StartsWith(
value(SomeClass+<>c__DisplayClass0).ComparisonProperty
)
AndAlso
subEntity.SubSubEntity.BarProperty == "Bar"
AndAlso
subEntity.SubSubEntity.SubSubSubEntity.Any(
subSubSubEntity => (x.SubSubSubSubEntity.BazProperty == "whatever")
)
)
)
Run Code Online (Sandbox Code Playgroud)
我试图按类型提取列表属性条件,即
TEntity : [ /* no conditions for immediate members of TEntity */ ]
TSubEntity : [ { SomeProperty == False } ]
TSubSubEntity : [ { FooProperty.StartsWith(/* ... */) },
{ BarProperty == "Bar" } ],
TSubSubSubEntity : [ /* no conditions for immediate members of TSubSubSubEntity */ …Run Code Online (Sandbox Code Playgroud) 我试图将lambda表达式中的参数类型从一种类型替换为另一种类型.
我在stackoverflow上找到了其他答案,即这个,但我没有运气.
想象一下你有一个域对象和一个存储库,你可以从中检索域对象.
但是,存储库必须处理自己的数据传输对象,然后映射并返回域对象:
ColourDto.cs
public class DtoColour {
public DtoColour(string name)
{
Name = name;
}
public string Name { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
DomainColour.cs
public class DomainColour {
public DomainColour(string name)
{
Name = name;
}
public string Name { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
Repository.cs
public class ColourRepository {
...
public IEnumerable<DomainColour> GetWhere(Expression<Func<DomainColour, bool>> predicate)
{
// Context.Colours is of type ColourDto
return Context.Colours.Where(predicate).Map().ToList();
}
}
Run Code Online (Sandbox Code Playgroud)
正如您所看到的,这将不起作用,因为谓词是针对域模型的,而存储库中的Collection是数据传输对象的集合.
我试图使用an ExpressionVisitor来做到这一点,但无法弄清楚如何只更改ParameterExpression没有例外被抛出的类型,例如:
测试场景
public class ColourRepository …Run Code Online (Sandbox Code Playgroud) 在有人喊出答案之前,请先阅读问题.
.NET 4.0的ExpressionVisitor中该方法的目的是什么:
public static ReadOnlyCollection<T> Visit<T>(ReadOnlyCollection<T> nodes, Func<T, T> elementVisitor)
Run Code Online (Sandbox Code Playgroud)
我对该方法的目的的第一个猜测是,它将访问nodes参数指定的每个树中的每个节点,并使用elementVisitor函数的结果重写树.
情况似乎并非如此.实际上这种方法似乎比什么都没有,除非我在这里遗漏了一些东西,我强烈怀疑我是......
我尝试在我的代码中使用此方法,当事情没有按预期工作时,我反映了方法并发现:
public static ReadOnlyCollection<T> Visit<T>(ReadOnlyCollection<T> nodes, Func<T, T> elementVisitor)
{
T[] list = null;
int index = 0;
int count = nodes.Count;
while (index < count)
{
T objA = elementVisitor(nodes[index]);
if (list != null)
{
list[index] = objA;
}
else if (!object.ReferenceEquals(objA, nodes[index]))
{
list = new T[count];
for (int i = 0; i < index; i++)
{
list[i] = nodes[i];
} …Run Code Online (Sandbox Code Playgroud) 我正在尝试编写一个ExpressionVisitor来包装我的LINQ到对象表达式,以自动使它们的字符串比较不区分大小写,就像它们在LINQ到实体中一样.
编辑:我确实想要使用ExpressionVisitor,而不是仅仅在创建它时为我的表达式应用一些自定义扩展或其他一些重要原因:传递给我的ExpressionVisitor的表达式是由ASP.Net Web API ODATA层生成的,所以我无法控制它是如何生成的(即我不能小写它正在搜索的字符串,除非在此ExpressionVisitor中).
必须支持LINQ to Entities.不只是扩展.
这是我到目前为止所拥有的.它在字符串上查找对"Contains"的调用,然后在该表达式中的任何成员访问上调用ToLower.
但是,它不起作用.如果我在更改后查看表达式,它看起来对我来说是正确的,所以我不确定我可能做错了什么.
public class CaseInsensitiveExpressionVisitor : ExpressionVisitor
{
protected override Expression VisitMember(MemberExpression node)
{
if (insideContains)
{
if (node.Type == typeof (String))
{
var methodInfo = typeof (String).GetMethod("ToLower", new Type[] {});
var expression = Expression.Call(node, methodInfo);
return expression;
}
}
return base.VisitMember(node);
}
private Boolean insideContains = false;
protected override Expression VisitMethodCall(MethodCallExpression node)
{
if (node.Method.Name == "Contains")
{
if (insideContains) throw new NotSupportedException();
insideContains = true;
var result = base.VisitMethodCall(node);
insideContains …Run Code Online (Sandbox Code Playgroud) 我正在寻找一种将条件表达式解析为字符串的方法.
我能想到的最好的例子是LINQ-to-SQL.它使用ExpressionVisitors格式化"Where"子句.例:
from a in b where a.x == 5 && a.y < 3 select a
Run Code Online (Sandbox Code Playgroud)
这将转换为以下字符串(大约,MSSQL对我来说不是最新的):
"SELECT * FROM b WHERE x = 5 AND y < 3"
Run Code Online (Sandbox Code Playgroud)
根据我的阅读,这是使用ExpressionVisitor类完成的,如本文所述:http://blogs.msdn.com/b/mattwar/archive/2007/07/31/linq-building-an-iqueryable -provider部分,ii.aspx
现在的问题是我不使用LINQ,但我需要这个特殊的功能.有没有办法解析这样的条件?我愿意用反思,代表,lambda等做任何事情.
老实说,我不认为这是可能的,但我的大脑有点油炸(读:如果问题很荒谬,那就好了),所以我想我也可以试试S/O.
编辑:最终用法示例:
// Usage:
foo.Bar(foo => foo.X == 5 && foo.Y < 3)
// Ideal string output (variable name (foo) is not needed):
"foo.X == 5 && foo.Y < 3"
Run Code Online (Sandbox Code Playgroud)
编辑2:是的,一个数字可以低于3并且等于5.告诉你我的大脑是油炸的.
我想结合两个LambdaExpressions而不编译它们.
这就是我编译它们时的样子:
public Expression<Func<TContainer,bool>> CreatePredicate<TContainer,TMember>(
Expression<Func<TContainer,TMember>> getMemberExpression,
Expression<Func<TMember,bool>> memberPredicateExpression)
{
return x => memberPredicateExpression.Compile()(getMemberExpression.Compile()(x));
}
Run Code Online (Sandbox Code Playgroud)
这显然不是从提供的参数中获取目标表达式的最快方法.此外,它使它与不支持C#方法调用的LINQ to SQL等查询提供程序不兼容.
从我所看到的,似乎最好的方法是建立一个ExpressionVisitor班级.然而,这似乎是一个非常常见的任务.有谁知道现有的开源代码库提供这种功能?如果没有,最好的方法ExpressionVisitor是尽可能使其尽可能通用?
该System.Linq.Expressions.ExpressionVisitor有一个名为方法VisitExtension,似乎什么也不做,除了调用VisitChildren的方法Expression被访问.
protected internal virtual Expression VisitExtension(Expression node)
{
return node.VisitChildren(this);
}
Run Code Online (Sandbox Code Playgroud)
我理解它的VisitChildren作用.我也明白这个虚拟实现可能并且可能意味着被覆盖.所以我从MSDN上的方法文档中收集到了,这些文字不用说文字和简短的评论:
访问扩展表达式的子项.可以重写此操作以访问或重写特定的扩展节点.如果没有覆盖它,这个方法将调用VisitChildren,它给节点一个机会来走它的孩子.默认情况下,VisitChildren将尝试减少节点.
我觉得这个解释没什么帮助.具体来说,让我从理解能力中解脱出来的短语是"或重写特定的扩展节点".
我理解其余部分,它涉及将表达式减少或分解为子表达式.
在同一名称空间中还有一个名为枚举的枚举ExpressionType,其目的非常清楚.但是在其所有成员中,有一个成员被命名为Extension我无法映射到我目前所知的任何语法令牌.
在这个例子中的文档也是令人沮丧的简洁.它描述的值Extension如下:
扩展表达式.
很明显,这两个- ExpressionType.Extension和ExpressionVisitor.VisitExtension-是相关的.
但什么是扩展?当然,很明显,扩展方法在这种情况下没有地位.表达式扩展在哪里引用哪个语法工件?
我正在尝试按照创建IQueryable LINQ提供程序的说明进行操作,但是当我实现ExpressionVisitor按照指示继承的类时,我被告知ExpressionVisitor由于其保护级别而无法访问.我错过了一些非常基本的东西吗?
expression iqueryable custom-linq-providers expressionvisitor
我正在使用ExpressionVisitor解析表达式树来查明它是否包含指定的参数.一旦找到参数,继续遍历是没有意义的.
是否有任何方法可以通过访问者模式停止遍历,更具体地说是ExpressionVisitor在.NET中?
这是我到目前为止所做的,它正如预期的那样工作.但是一旦布尔标志设置为true,就此算法而言,停止遍历是有意义的.
public class ExpressionContainsParameterVisitor : ExpressionVisitor
{
private bool expressionContainsParameter_;
private ParameterExpression parameter_;
public bool Parse(Expression expression, ParameterExpression parameterExpression)
{
parameter_ = parameterExpression;
expressionContainsParameter_ = false;
Visit(expression);
return expressionContainsParameter_;
}
protected override Expression VisitParameter(ParameterExpression node)
{
if (node == parameter_)
{
expressionContainsParameter_ = true;
}
return node;
}
}
Run Code Online (Sandbox Code Playgroud) Asume,我们这样的表达式:
someIQueryable.Where(x => x.SomeBoolProperty)
someIQueryable.Where(x => !x.SomeBoolProperty)
Run Code Online (Sandbox Code Playgroud)
我需要像上面这样的表达式转换(使用表达式访问器重写)表达式:
someIQueryable.Where(x => x.SomeBoolProperty == true)
someIQueryable.Where(x => x.SomeBoolProperty != true)
Run Code Online (Sandbox Code Playgroud)
注意:如果我们有更复杂的表达式,重写器也必须在更一般的情况下工作:
someIQueryable.Where((x => x.SomeBoolProperty && x.SomeIntProperty > 0) || !x.SomeOtherBoolProperty))
Run Code Online (Sandbox Code Playgroud)