代码之家  ›  专栏  ›  技术社区  ›  bit

在cosmossdb上的linq.where()内使用linq.any()。

  •  3
  • bit  · 技术社区  · 6 年前

    我想套一个 .Any() A内 .Where() 用于查询本地COSMOSDB仿真器的子句。

    代码如下;其中 permittedStundentIds 是一个变量( List<long> ) a 是一个 Document 在宇宙中

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

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

    不支持方法“any”。活动ID: 80000A8-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));
    

    我知道某些lambda扩展在用于cosmossdb的SQL API上尚不受支持,但是否有解决方法?

    2 回复  |  直到 6 年前
        1
  •  2
  •   abatishchev Marc Gravell    6 年前

    在尝试了各种lambda表达式的大量组合之后,下面是我的解决方案。

    我添加了一个 StudentIds 我的财产 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);
        }
    }
    
        2
  •  0
  •   Harald Coppoolse    6 年前

    所以你有一系列的许可证和一系列学生的文件。每个学生都有一个身份证。

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

    换句话说,如果permittedStudentids的值为1、2,则需要知道文档中是否有任何学生。ID为1或2的学生。

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

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

    如果两个序列都是可查询的,那么这就可以工作,但是如果您的文档也是可查询的,那么它也应该工作。学生是可查询的,而您的permittedStudentids是IEnumerable。我的最佳猜测是,这将成为一个SQL包含。见 Queryable.Intersect