Используйте Linq.Any () внутри Linq.Where () в CosmosDb

Я пытаюсь вложить .Any() в предложение .Where() для запроса локального эмулятора CosmosDb.

Код выглядит следующим образом: где permittedStundentIds - переменная (List<long>), а a - Document в CosmosDb

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

Когда я выполняю запрос, я получаю сообщение об ошибке:

Метод «Любой» не поддерживается. ActivityId: 800000a8-0002-d600-b63f-84710c7967bb, documentdb-dotnet-sdk / 1.22.0 Host / 64-разрядная версия MicrosoftWindowsNT / 10.0.16299.0

Я пробовал несколько вариантов, чтобы получить эквивалентное выражение, но безрезультатно. Единственное, что сработало, - это использование .Contains() и жесткое кодирование индекса студентов; что невозможно, поскольку количество студентов может быть неизвестно.

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

Я понимаю, что некоторые лямбда-расширения еще не поддерживаются в Sql API для CosmosDb, но есть ли обходной путь для этого?


person bit    schedule 08.07.2018    source источник
comment
Можно ли заменить Any на Where и добавить Count ()! = 0?   -  person Hans Kesting    schedule 08.07.2018
comment
Я пробовал это a => a.Students.Where(s => permittedStudentIds.Where(pSId => pSId == s.Id).Count() > 0).Count() > 0. Не работает. Выдает аналогичное сообщение об ошибке, относящееся к Where не поддерживается   -  person bit    schedule 08.07.2018
comment
Можете ли вы вместо этого попробовать Exists и посмотреть, работает ли он   -  person sharmav1    schedule 08.07.2018
comment
Пробовал a => permittedStudentIds.Exists(sId => a.Students.Exists(s => s.Id == sId)), не работает. Похожая ошибка.   -  person bit    schedule 08.07.2018
comment
Из того, что я вижу в Списке поддерживаемые операторы LINQ, проблема точно такая же, как и в C # LINQ. Любой не работает с DocumentDb CreateDocumentQuery   -  person Ivan Stoev    schedule 08.07.2018
comment
Я знаю, я только искал работу ...   -  person bit    schedule 08.07.2018


Ответы (2)


После того, как я опробовал многочисленные комбинации различных лямбда-выражений, вот что сработало для меня.

Я добавил свойство 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);
    }
}
person bit    schedule 08.07.2018
comment
Интересно. StudentIds действительно нужен? Я имею в виду, интересно, работает ли то же решение с Or предикатом построения, но с использованием a.Students.Select(s => s.Id).Contains(…)? - person Ivan Stoev; 08.07.2018

Итак, у вас есть последовательность разрешенныхStudentIds и Документ с последовательностью Student. У каждого студента есть идентификатор.

Вы хотите знать, есть ли какие-либо разрешенныеStudentsId, которые также являются идентификаторами одного (или нескольких) студентов вашего документа.

Другими словами, если разрешенныйStudentIds имеет значения 1, 2, вы хотите знать, есть ли в Document.Students какой-либо студент с идентификатором 1 или 2.

Почему бы не извлечь идентификаторы всех студентов, пересечь их с вашими разрешенными идентификаторами студентов и посмотреть, будет ли результат пустым или нет?

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-файлом. См. Запрашиваемый. Пересечение

person Harald Coppoolse    schedule 09.07.2018
comment
Что не работает? Ваш IQueryable не позволяет Intersect или Intersect не дает желаемых результатов? - person Harald Coppoolse; 09.07.2018
comment
Не допускает пересечения. - person bit; 09.07.2018