首页 文章

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

提问于
浏览
2

我试图在 .Where() 子句中嵌套 .Any() 来查询本地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

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

    我在 DocumentModel 课程中添加了一个 StudentIds 属性;冗余但仅用于过滤 .

    此后,我 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);
        }
    }
    
  • 1

    因此,您有一系列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

相关问题