请您帮忙回顾一下似乎都不起作用的表达方式吗?
试图在记录属性@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)。这种转换仅限于专门为处理而编写的方法(和运算符等),当您将其带来一些不理解的东西(例如您自己的扩展方法)时,它会抛出异常以表明您“已经堕落”边缘”。
[大多数LINQ提供程序都理解模式var personNames = new[]{ "Walter" }; something .Where(person => personNames.Contains(person))
来执行您想要的操作-即Enumerable.Contains用于执行IN
/任何查询。我认为构造使用此方法的表达式树将起作用。仅在LINQ查询中直接使用它而不是自定义方法就可以在您的示例中使用。似乎没有办法扩展LINQ提供程序来教它如何翻译您的方法。
我有些运气,将@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);
}