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);
}
能否请您帮忙检查一下似乎都不起作用的表达方式?
正在尝试查找记录 属性 @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);
}