Как я могу сделать возможным использование динамической лямбды в Dynamic LINQ? - PullRequest
3 голосов
/ 01 марта 2011

В своем поиске парсера выражений я нашел 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'

Второй синтаксис, однако, позволил бы мне вкладывать лямбда-код (который является моим окончательнымцель) вот так:

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

Кто-нибудь знает, как настроить Dynamic.cs для поддержки этого синтаксиса?

1 Ответ

2 голосов
/ 01 марта 2011

После нескольких часов отладки я сам нашел решение.

Теперь успешно проходит следующий модульный тест:

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) Расширить TokenId перечисления с помощью члена 'Lambda'

2) Добавить внутреннее имя IDictionary в класс ExpressionParser.Инициализируйте его в конструкторе 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;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...