在CosmosDb的Linq.Where()中使用Linq.Any()

时间:2018-07-08 05:27:13

标签: c# linq azure-cosmosdb azure-cosmosdb-sqlapi

我试图在.Any()子句中嵌套.Where(),以查询本地CosmosDb模拟器。

代码如下所示;其中permittedStundentIds是变量(List<long>),a是CosmosDb中的Document

.Where(a => permittedStudentIds.Any(sId => a.Students.Any(s => s.Id == sId)));

执行查询时,出现错误:

  

不支持方法“任何”。 ActivityId:   800000a8-0002-d600-b63f-84710c7967bb,documentdb-dotnet-sdk / 1.22.0   主机/ 64位MicrosoftWindowsNT / 10.0.16299.0

我尝试了多种变体来获得等效的表达式,但无济于事。唯一有效的方法是使用.Contains()并对学生索引进行硬编码。这是不可行的,因为学生人数可能未知。

.Where(a => permittedStudentIds.Contains(a.Students[0].Id));

我确实知道CosmosDb的Sql API尚不支持某些lambda扩展,但是有解决方法吗?

2 个答案:

答案 0 :(得分:2)

尝试了各种lambda表达式的多种组合之后,这对我有用。

我在DocumentModel类中添加了OR-ed属性;多余,但仅用于过滤。

此后,我使用.Contains() Expression<Func<MyDocumentModel, bool>> query = a => a.StudentIds.Contains(permittedStudentIds[0]); foreach (var id in permittedStudentIds.Skip(1)) { query = query.Or(a => a.StudentIds.Contains(id)); } 进行查询,如下所示:

.Where(query);

,然后使用如下查询:

query.Or()

对于// See: https://blogs.msdn.microsoft.com/meek/2008/05/02/linq-to-entities-combining-predicates/ public static class ExpressionExtensions { public static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge) { // build parameter map (from parameters of second to parameters of first) var map = first.Parameters.Select((f, i) => new { f, s = second.Parameters[i] }).ToDictionary(p => p.s, p => p.f); // replace parameters in the second lambda expression with parameters from the first var secondBody = ParameterVistor.ReplaceParameters(map, second.Body); // apply composition of lambda expression bodies to parameters from the first expression return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters); } public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second) { return first.Compose(second, Expression.AndAlso); } public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second) { return first.Compose(second, Expression.OrElse); } } public class ParameterVistor : ExpressionVisitor { private readonly Dictionary<ParameterExpression, ParameterExpression> map; public ParameterVistor(Dictionary<ParameterExpression, ParameterExpression> map) { this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>(); } public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp) { return new ParameterVistor(map).Visit(exp); } protected override Expression VisitParameter(ParameterExpression p) { ParameterExpression replacement; if (map.TryGetValue(p, out replacement)) { p = replacement; } return base.VisitParameter(p); } } 部分,我使用了以下类:

ini_set("log_errors", TRUE);
ini_set("error_log", get_template_directory_uri() . "/errors.log");

答案 1 :(得分:0)

因此,您具有一系列的allowedStudentIds和具有一系列学生的文档。每个学生都有一个ID。

您想知道是否有任何允许的StudentId也是您文档中一个(或多个)学生的ID。

换句话说,如果allowedStudentIds的值为1、2,则您想知道文档中是否有任何Students.students的学生。

为什么不提取所有学生的ID,将其与您允许的学生ID相交,看看结果是否为空?

var studentIds = Document.Students.Select(student => student.Id);
var intersection = studentIds.Intersect(permittedStudentIds);
var result = intersection.Any();
// TODO: make one statement.

如果两个序列都是可查询的,则此方法有效,但是如果您的Document.Students是IQueryable且您的allowedStudentIds是IEnumerable,则也应适用。我最好的猜测是,这将成为SQL包含的内容。参见Queryable.Intersect