Пользовательское выражение для RavenDB, где предложение: поле содержит любой элемент из предоставленного массива - PullRequest
1 голос
/ 08 октября 2019

Не могли бы вы помочь в рассмотрении выражений, которые, похоже, не работают?

Попытка найти записи, в которых свойство записи @In предоставляет предоставленный массив значений.

RavenDB 3.5 вызывает исключение, котороеему не удалось понять указанное выражение.

Я пытаюсь избежать использования клиента sdk и разрешить его с помощью пользовательских выражений, либо с аналогичным расширением sdk .In, либо Enumerable.Any.

* 1011. * Любая помощь приветствуется
System.NotSupportedException: 'Could not understand expression
void Main()
{
    var exp = ByPersonNameIn(new[] { "Walter" });
    //var exp = ByPersonNameAny(new[] { "Walter" });

    var persons = new List<Person>()
    {
        new Person { Name = "Walter" },
        new Person { Name = "Jesse" },
        new Person { Name = "Walter" },
    };

    // Expression works here, but not at IRavenQueryable.Where
    var res = persons.Where(exp.Compile()).ToList();
    res.Dump(nameof(res));
}

private Expression<Func<Person, bool>> ByPersonNameIn(string[] names)
{
    var person = Expression.Parameter(typeof(Person), "person");
    var personProp = Expression.PropertyOrField(person, nameof(Person.Name));
    var namesParam = Expression.Constant(names, typeof(string[]));

    var @in = typeof(StringExt).GetMethod("In", new[] { typeof(string), typeof(IEnumerable<string>) });
    var inExp = Expression.Call(@in, personProp, namesParam);

    return Expression.Lambda<Func<Person, bool>>(inExp, person);
}

private Expression<Func<Person, bool>> ByPersonNameAny(string[] names)
{
    var person = Expression.Parameter(typeof(Person), "person");
    var name = Expression.Parameter(typeof(string), "name");
    var namesParam = Expression.Constant(names, typeof(string[]));

    var @eq = Expression.Equal(name, (Expression.PropertyOrField(person, nameof(Person.Name))));

    var @any = Expression.Call(typeof(Enumerable), "Any", new[] { typeof(string) }, namesParam, Expression.Lambda(@eq, name));

    return Expression.Lambda<Func<Person, bool>>(@any, person);
}

public class Person
{
    public string Name {get;set;}
}

public static class StringExt
{
    public static bool In(this string field, IEnumerable<string> values)
    {
        return values.Any(value => field.Equals(value));
    }

    public static bool In(this string field, params string[] values)
    {
        return values.Any(value => field.Equals(value));
    }
}

Ответы [ 2 ]

0 голосов
/ 08 октября 2019

У меня была некоторая "удача", имитирующая @In как OR, которая не идеальна, но должна работать пока, пока я не найду что-то лучшее.

        private static Expression<Func<TDocument, bool>> ByPropertyViaEqOr<TDocument>(string propName, string[] values)
        {
            var doc = Expression.Parameter(typeof(TDocument), "doc");
            var eq = values.Select(value => ByPropertyViaEq<TDocument>(propName, value));
            var orElse = eq.Aggregate((l, r) => Expression.OrElse(l, r));

            return Expression.Lambda<Func<TDocument, bool>>(orElse, doc);
        }

        private static Expression ByPropertyViaEq<TDocument>(string propName, string value)
        {
            var doc = Expression.Parameter(typeof(TDocument), "doc");
            var docProp = Expression.PropertyOrField(doc, propName);
            var propValue = Expression.Constant(value, typeof(string));

            return Expression.Equal(docProp, propValue);
        }
0 голосов
/ 08 октября 2019

Библиотека RavenDB имеет код в своем поставщике LINQ для перевода запросов на IRavenQueryable в запросы, которые RavenDB может понять (RQL). Этот перевод ограничен методами (и операторами, и т. Д.), Которые он специально написан для обработки, и когда вы приносите что-то, чего он не понимает, например, свой собственный метод расширения, то он выдает исключение, чтобы указать, что вы «упали». «на грани».

Большинство провайдеров LINQ понимают шаблон var personNames = new[]{ "Walter" }; something .Where(person => personNames.Contains(person)), чтобы делать то, что вы хотите - то есть, где Enumerable.Contains используется для выполнения IN / любого запроса. Я думаю, что построение дерева выражений для использования этого метода будет работать. Простое использование его в запросе LINQ напрямую вместо пользовательского метода также будет работать в вашем примере. Похоже, нет способа расширить поставщика LINQ, чтобы научить его переводить ваш метод.

...