【问题标题】:How can I make it possible to use a dynamic Lambda in Dynamic LINQ如何使在动态 LINQ 中使用动态 Lambda 成为可能
【发布时间】:2011-03-01 09:43:58
【问题描述】:

在搜索表达式解析器时,我找到了Dynamic 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)     // OK

进入

list.Any(i => i == x) // Raises error: No property or field 'i' exists in type 'int'

然而,第二种语法允许我像这样嵌套 lambda(这是我的最终目标):

list1.All(i => list2.Any(j => j == i))

有人知道如何调整 Dynamic.cs 以支持这种语法吗?

【问题讨论】:

  • 您是否尝试过单独构建 lambda 并将嵌套的一个作为参数传递?没试过,但值得一试

标签: linq dynamic lambda


【解决方案1】:

经过几个小时的调试,我自己找到了解决方案。

下面的单元测试现在成功了:

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));

我对示例 Dynamic.cs 文件所做的更改:

1) 使用成员 'Lambda' 扩展枚举 TokenId

2) 向 ExpressionParser 类添加一个名为 internals 的 IDictionary。在 ExpressionParser 构造函数中初始化它

3) 替换(从第 971 行开始)

if (symbols.TryGetValue(token.text, out value) ||
    externals != null && externals.TryGetValue(token.text, out value)) {

if (symbols.TryGetValue(token.text, out value) ||
externals != null && externals.TryGetValue(token.text, out value) ||
internals.TryGetValue(token.text, out value)) {

4) 替换(从第 1151 行开始)

if (member == null)
    throw ParseError(errorPos, Res.UnknownPropertyOrField,
       id, GetTypeName(type));

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) 替换(从第 1838 行开始)

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;

【讨论】:

  • 我也实现了这些更改。需要与您描述的类似的功能。谢谢,这为我节省了数小时的痛苦。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-08-22
  • 1970-01-01
  • 2011-09-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多