在我搜索表达式解析器时,我发现动态LINQ API 。我想使用这个API让最终用户指定一些用于验证业务对象的标准。
所以我第一次尝试使用库来完成以下单元测试
var x = Expression.Parameter(typeof(int),x); var list = Expression.Parameter(typeof(List< int>),list); var e = DynamicExpression.ParseLambda(new [] {x,list},null,list.Any(it == x)); var compiledExpression = e.Compile(); var myList = new List< int> {24,46,67,78}; Assert.AreEqual(false,compiledExpression.DynamicInvoke(2,myList)); Assert.AreEqual(true,compiledExpression.DynamicInvoke(24,myList));然而,我想要稍微更复杂的语法,因为我想更改这个
list.Any(it == x)// OKinto
list.Any(i => i == x)//引发错误:类型'int'中没有属性或字段'i'然而,第二种语法允许我嵌套lambda(这是我的最终目标),如下所示:
list1.All(i => list2.Any (j => j == i))任何人都知道如何调整Dynamic.cs来支持这个语法?
解决方案经过几个小时的调试,我自己找到了解决方案。
以下单元测试成功:
var list1 = Expression.Parameter(typeof(List< int> 列表1); var list2 = Expression.Parameter(typeof(List< int>),list2); var e = DynamicExpression.ParseLambda(new [] {list1,list2},null,list2.All(i => list1.Any(j => j == i))); var compiledExpression = e.Compile(); var myList1 = new List< int> {24,46,67,78}; var myList2 = new List< int> {46}; var myList3 = new List< int> {8}; Assert.AreEqual(true,compiledExpression.DynamicInvoke(myList1,myList2)); Assert.AreEqual(false,compiledExpression.DynamicInvoke(myList1,myList3));我应用于示例Dynamic.cs文件的更改:
1)使用成员Lambda扩展枚举TokenId
2)将名为内部的IDictionary添加到ExpressionParser类中。在ExpressionParser构造函数中初始化
3)替换(从第971行开始)
if(symbols.TryGetValue(token.text,out value)|| externals!= null&& externals.TryGetValue(token.text,out value)){与
if symbol.TryGetValue(token.text,out value)|| externals!= null&& externals.TryGetValue(token.text,out value)|| internals.TryGetValue(token.text,出价值)){4)替换(从第1151行开始)
if(member == null) throw ParseError(errorPos,Res.UnknownPropertyOrField, id,GetTypeName(type));与
if(member == null) { if(token.id == TokenId.Lambda&& it.Type == type) { //这可能是在lambda表达式中使用的内部变量,因此可以像这样存储 internals.Add(id,it); NextToken(); var right = ParseExpression(); return right; } else { throw ParseError(errorPos,Res.UnknownPropertyOrField, id,GetTypeName(type)); } }5)替换(从1838开始) p>
case'=': NextChar(); if(ch =='='){ NextChar(); t = TokenId.DoubleEqual; } else { t = TokenId.Equal; } break;与
case'=': NextChar(); if(ch =='='){ NextChar(); t = TokenId.DoubleEqual; } else if(ch =='>') { NextChar(); t = TokenId.Lambda; } else { t = TokenId.Equal; } break;
In my search for a expression parser, I found the Dynamic LINQ API. I want to use this API for letting the end user specify some criteria for validation of the business objects.
So my first attempt to use the library succeeds with the following Unit test
var x = Expression.Parameter(typeof(int), "x"); var list = Expression.Parameter(typeof(List<int>), "list"); var e = DynamicExpression.ParseLambda(new[] { x, list }, null, "list.Any(it == x)"); var compiledExpression = e.Compile(); var myList = new List<int> { 24, 46, 67, 78 }; Assert.AreEqual(false, compiledExpression.DynamicInvoke(2, myList)); Assert.AreEqual(true, compiledExpression.DynamicInvoke(24, myList));However I want to have a slightly more complex syntax as I want to change this
list.Any(it == x) // OKinto
list.Any(i => i == x) // Raises error: No property or field 'i' exists in type 'int'The second syntax would however allow me to nest lambda's (which is my ultimate goal) like so:
list1.All(i => list2.Any(j => j == i))Anyone know how to adjust Dynamic.cs to support this syntax?
解决方案After some hours of debugging I found the solution myself.
The following unit test succeeds now:
var list1 = Expression.Parameter(typeof(List<int>), "list1"); var list2 = Expression.Parameter(typeof(List<int>), "list2"); var e = DynamicExpression.ParseLambda(new[] { list1, list2 }, null, "list2.All(i => list1.Any(j => j == i))"); var compiledExpression = e.Compile(); var myList1 = new List<int> { 24, 46, 67, 78 }; var myList2 = new List<int> { 46 }; var myList3 = new List<int> { 8 }; Assert.AreEqual(true, compiledExpression.DynamicInvoke(myList1, myList2)); Assert.AreEqual(false, compiledExpression.DynamicInvoke(myList1, myList3));The changes I have applied to the example Dynamic.cs file:
1) Extend the enum TokenId with the member 'Lambda'
2) Add an IDictionary named internals to class ExpressionParser. Initialize it in the ExpressionParser constructor
3) Replace (starting on line 971)
if (symbols.TryGetValue(token.text, out value) || externals != null && externals.TryGetValue(token.text, out value)) {with
if (symbols.TryGetValue(token.text, out value) || externals != null && externals.TryGetValue(token.text, out value) || internals.TryGetValue(token.text, out value)) {4) Replace (starting on line 1151)
if (member == null) throw ParseError(errorPos, Res.UnknownPropertyOrField, id, GetTypeName(type));with
if (member == null) { if(token.id == TokenId.Lambda && it.Type == type) { // This might be an internal variable for use within a lambda expression, so store it as such internals.Add(id, it); NextToken(); var right = ParseExpression(); return right; } else { throw ParseError(errorPos, Res.UnknownPropertyOrField, id, GetTypeName(type)); } }5) Replace (starting on line 1838)
case '=': NextChar(); if (ch == '=') { NextChar(); t = TokenId.DoubleEqual; } else { t = TokenId.Equal; } break;with
case '=': NextChar(); if (ch == '=') { NextChar(); t = TokenId.DoubleEqual; } else if(ch == '>') { NextChar(); t = TokenId.Lambda; } else { t = TokenId.Equal; } break;
更多推荐
如何在动态LINQ中使用动态Lambda
发布评论