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

问题描述 投票:1回答:2

请您帮忙回顾一下似乎都不起作用的表达方式吗?

试图在记录属性@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));
    }
}
c# linq lambda expression ravendb
2个回答
0
投票

RavenDB库在其LINQ提供程序中具有代码,可以将IRavenQueryable上的查询转换为RavenDB可以理解的查询(RQL)。这种转换仅限于专门为处理而编写的方法(和运算符等),当您将其带来一些不理解的东西(例如您自己的扩展方法)时,它会抛出异常以表明您“已经堕落”边缘”。

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


0
投票

我有些运气,将@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);
        }
© www.soinside.com 2019 - 2024. All rights reserved.