RavenDB where 子句的自定义表达式:字段包含提供的数组中的任何项目

Custom expression for RavenDB where clause: field contains any item from provided array

能否请您帮忙检查一下似乎都不起作用的表达方式?

正在尝试查找记录 属性 @In 提供的值数组。

RavenDB 3.5 抛出无法理解指定表达式的异常。

我试图避免使用客户端 sdk 并使用自定义表达式解决此问题,使用类似的 sdk .In 扩展名或 Enumerable.Any.

感谢任何帮助

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

RavenDB 库在其 LINQ 提供程序中具有代码,可将 IRavenQueryable 上的查询转换为 RavenDB 可以理解的查询 (RQL)。这种翻译仅限于它专门编写来处理的方法(和运算符等),当你给它带来它不理解的东西时,比如你自己的扩展方法,然后它会抛出异常来表明你有 "fallen off the edge".

大多数 LINQ 提供程序理解模式 var personNames = new[]{ "Walter" }; something .Where(person => personNames.Contains(person)) 来执行您想要的操作 - 即 Enumerable.Contains 用于执行 IN/any 查询。我认为构造表达式树以使用此方法会起作用。直接在 LINQ 查询中直接使用它而不是自定义方法也可以在您的示例中使用。似乎没有办法扩展 LINQ 提供程序来教它如何翻译您的方法。

我有一些 "luck" 模仿 @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);
        }