Сгенерируйте запрос Dynamic Linq, используя externalIt

Я использую библиотеку Microsoft Dynamic Linq (System.Linq.Dynamic) для создания некоторых запросов во время выполнения. Это отлично сработало для меня, но для одного конкретного сценария.

Упрощенный сценарий. Я пытаюсь запросить все претензии, которые имеют определенные теги, выбранные пользователем, и баланс которых больше некоторого числа.

static void Main(string[] args)
    {
        var claims = new List<Claim>();
        claims.Add(new Claim { Balance = 100, Tags = new List<string> { "Blah", "Blah Blah" } });
        claims.Add(new Claim { Balance = 500, Tags = new List<string> { "Dummy Tag", "Dummy tag 1" } });

        // tags to be searched for
        var tags = new List<string> { "New", "Blah" };
        var parameters = new List<object>();
        parameters.Add(tags);

        var query = claims.AsQueryable().Where("Tags.Any(@0.Contains(outerIt)) AND Balance > 100", parameters.ToArray());
    }

public class Claim
{
    public decimal? Balance { get; set; }
    public List<string> Tags { get; set; }
}

Этот запрос выдает ошибку:

Необработанное исключение типа «System.Linq.Dynamic.ParseException» произошло в System.Linq.Dynamic.dll. Дополнительная информация: в типе «String» не существует свойства или поля «Баланс».

Динамический синтаксический анализатор linq, похоже, пытается найти свойство Balance в теге, а не в объекте Claim.

  • Я пытался поиграть с ключевыми словами outerIt, innerIt, It в Dynamic Linq, но ничего из этого не работает.
  • Изменение последовательности работает, но для меня это не вариант, так как в реальном приложении фильтры, операторы и шаблоны будут динамическими (настраиваются конечным пользователем).
  • Боксирование условий в скобках (), также не помогает.
  • Обходной путь — создайте простое условие «содержит» для каждого выбранного тега, например. Tags.Contains("New") ИЛИ Tags.Contains("Blah") и т.д.. Но в реальном приложении это приводит к действительно сложному/плохому запросу для каждого условия и снижает производительность.

Я мог что-то упустить или это может быть ошибка в библиотеке.

Я был бы очень признателен, если бы кто-нибудь помог мне с этим.


person Amanvir Mundra    schedule 07.04.2017    source источник
comment
где вы узнали такой синтаксис, какие-либо ссылки на документы?   -  person Lei Yang    schedule 07.04.2017
comment
Это была моя отправная точка: weblogs.asp.net/scottgu/. Затем был другой вопрос SO, откуда я получил ссылки: предложение с любым"> stackoverflow.com/questions/37740409/   -  person Amanvir Mundra    schedule 07.04.2017
comment
Насколько я понимаю, большинство ключевых слов, которые работают для Linq/EF, должны использоваться для динамического linq с небольшой синтаксической разницей.   -  person Amanvir Mundra    schedule 07.04.2017
comment
но в предоставленных вами ссылках на документы я не видел синтаксиса @n как левого значения, это может быть только правильное значение.   -  person Lei Yang    schedule 07.04.2017
comment
@n слева преобразует его в оператор SQL IN(values). stackoverflow.com/questions/15633066/   -  person Amanvir Mundra    schedule 07.04.2017
comment
@AmanvirSinghMundra скажу, что в исходной библиотеке есть ошибка   -  person xanatos    schedule 07.04.2017
comment
Я думаю, что outerIt является объектом Claim, как List<string> может его содержать?   -  person Lei Yang    schedule 07.04.2017
comment
@LeiYang externalIt является объектом Tag, поскольку перед ним стоит оператор Any. Думайте об этом как о области действия лямбда-переменной. Если вы попробуете запустить запрос без условия Balance, то он сработает (только с условием Tag).   -  person Amanvir Mundra    schedule 07.04.2017
comment
вы написали «Microsoft Dynamic Linq», да, это было написано каким-то сотрудником Microsoft, но библиотека официально не поставляется с .net framework, не так ли? и можете ли вы предоставить более реальный сценарий, почему вы должны использовать такой динамический linq?   -  person Lei Yang    schedule 07.04.2017
comment
Он поставляется в виде пакета nuget, написанного Microsoft, и имеет открытый исходный код. Различные его вкусы используются многими поставщиками, например. Kendo UI, Telerik (насколько я знаю).   -  person Amanvir Mundra    schedule 07.04.2017
comment
@LeiYang Это тот же комментарий, который я собирался сделать, но: nuget.org/packages/System.Linq.Dynamic ... Авторы: Microsoft, описание: Это сборка Microsoft для функциональности динамического языка .Net 4.0.   -  person xanatos    schedule 07.04.2017
comment
какую проблему вы хотите решить (с помощью С#, а не linq)?   -  person Lei Yang    schedule 07.04.2017
comment
@LeiYang, упомянутый в вопросе. Пользователь настраивает определенное условие через пользовательский интерфейс, и мой код динамически генерирует для него запрос.   -  person Amanvir Mundra    schedule 07.04.2017
comment
какая часть вашего запроса вводится пользователем во время выполнения? если это имеет значение, вы не должны опускать код.   -  person Lei Yang    schedule 07.04.2017


Ответы (2)


Обнаружена ошибка в ParseAggregate... Перемещение itouterIt и обратно не работает, если есть несколько уровней. Код предполагает, что it и outerIt не будут изменены третьей стороной до сброса (технически код не является реентерабельным). Вы можете попробовать другие варианты System.Linq.Dynamic (есть два или три варианта). Вероятно, в некоторых вариантах это уже исправлено.

Или вы можете взять код со связанного сайта и перекомпилировать его внутри своего кода (в конце концов, «оригинальный» System.Linq.Dynamic — это один файл cs), и вы можете исправить его следующим образом:

Expression ParseAggregate(Expression instance, Type elementType, string methodName, int errorPos)
{
    // Change starts here
    var originalIt = it;
    var originalOuterIt = outerIt;
    // Change ends here

    outerIt = it;
    ParameterExpression innerIt = Expression.Parameter(elementType, elementType.Name);
    it = innerIt;
    Expression[] args = ParseArgumentList();

    // Change starts here
    it = originalIt;
    outerIt = originalOuterIt;
    // Change ends here

    MethodBase signature;
    if (FindMethod(typeof(IEnumerableSignatures), methodName, false, args, out signature) != 1)

Я уже открыл вопрос с предложенным исправлением ошибки в github проекта.

person xanatos    schedule 07.04.2017
comment
Я исправлю файл с вашим исправлением и дам вам знать, если это решит проблему. Большое спасибо. - person Amanvir Mundra; 07.04.2017
comment
Это работает. Спасибо, чувак, ты сэкономил мне недели труда. Я не совсем понимаю природу исправления в целом, поэтому я не уверен, на что может повлиять это исправление. Имеет ли смысл зарегистрировать дефект в репозитории github, чтобы он был исправлен в любых следующих выпусках? - person Amanvir Mundra; 07.04.2017
comment
@AmanvirSinghMundra 2 минуты назад я обновил свой пост: Я уже открыл вопрос с предложенным исправлением ошибки в github проекта. - person xanatos; 07.04.2017
comment
К сожалению, вы уже сделали это, пропустили последнее предложение. Спасибо еще раз - person Amanvir Mundra; 07.04.2017

Кажется, это работает правильно в моей версии: System.Linq.Dynamic.Core

См. тест здесь: https://github.com/StefH/System.Linq.Dynamic.Core/blob/master/test/System.Linq.Dynamic.Core.Tests/ComplexTests.cs#L19

person Stef Heyenrath    schedule 09.04.2017