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

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

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

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

当我执行查询时,我收到错误:

方法’Any’不受支持。 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扩展,但有没有解决方法呢?

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

我在我的DocumentModel类中添加了一个StudentIds属性; 冗余但仅用于过滤。

之后,我使用.Contains()对查询进行OR-ed ,如下所示:

 Expression> 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 Compose(this Expression first, Expression second, Func 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 => ps, p => pf); // 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(merge(first.Body, secondBody), first.Parameters); } public static Expression> And(this Expression> first, Expression> second) { return first.Compose(second, Expression.AndAlso); } public static Expression> Or(this Expression> first, Expression> second) { return first.Compose(second, Expression.OrElse); } } public class ParameterVistor : ExpressionVisitor { private readonly Dictionary map; public ParameterVistor(Dictionary map) { this.map = map ?? new Dictionary(); } public static Expression ReplaceParameters(Dictionary 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); } } 

因此,您有一系列allowedStudentIds和一个包含学生序列的文档。 每个学生都有一个Id。

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

换句话说,如果allowedStudentIds的值为1,2,则您想知道Document.Students中是否有ID为1或2的学生。

为什么不提取所有学生的ID,将它们与您的allowedStudentIds相交并查看结果是否为空?

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

如果两个序列都是AsQueryable,则它可以工作,但如果您的Document.Students是IQueryable并且您的allowedStudentIds是IEnumerable,它也可以工作。 我最好的猜测是,这将成为一个SQL包含。 请参见Queryable.Intersect