Запросы на IQueryable с именем и типом свойства во время выполнения - PullRequest
0 голосов
/ 28 июня 2019

Мне нужно выполнять запросы, зная имя и тип свойства во время выполнения.Я использовал рефлексию на IEnumerable<>, но будут ли проблемы с производительностью из-за этого?

Я хотел бы знать, есть ли лучший способ сделать это с IQueryable<>?Я немного посмотрел на Expressions, но я не совсем уверен, как это сделать.

Редактировать:

На данный момент это не кажетсябыть проблемой производительности, но я еще не тестировал с очень большими рабочими нагрузками.

Мне нужно искать в нескольких полях разных типов, известных во время выполнения.

var cosmosClient = new DocumentClient(new Uri(cosmosDBEndpointUrl), cosmosDBAuthorizationKey);
var feedOptions = new FeedOptions { MaxItemCount = -1, EnableCrossPartitionQuery = true };
var objects = cosmosClient.CreateDocumentQuery<MyObject>(collectionLink, feedOptions).AsEnumerable();

if (!string.IsNullOrEmpty(searchQuery))
{         
    var predicate = PredicateBuilder.New<MyObject>(); 

    foreach (var fieldToSearch in fieldsToSearch)
    {
      predicate = predicate.Or(x => x.GetPropertyValue(fieldToSearch).CheckDateTime().ToString()
                                     .Contains(searchQuery, StringComparison.InvariantCultureIgnoreCase));
      objects = objects.Where(predicate);
    }
}

objects = objects.Skip(index)
                 .Take(pageSize);

return objects.ToList();

И этот помощникМетод:

public static object GetPropertyValue(this object obj, string propertyName)
{
    foreach (var part in propertyName.Split('.'))
    {
        if (obj == null) { return null; }

        Type type = obj.GetType();
        PropertyInfo info = type.GetProperty(part, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
        if (info == null) { return null; }

        obj = info.GetValue(obj, null);
    }
    return obj;
}

В данном конкретном случае я не знаю, есть ли лучший способ сделать это.

1 Ответ

1 голос
/ 08 июля 2019

Так что в этом случае Я думаю, что вы хотите IQueryable вместо IEnumerable.Самый простой способ запомнить это то, что IEnumerable загружает все данные в память, а затем выполняет остальную часть вашего запроса LINQ, в то время как IQueryable пытается запустить ваши фильтры на источнике, если он может затем загрузить полученные данные в память.Вам необходимо использовать ORM, который поддерживает IQueriable, такой как LINQ to SQL или Entity Framework.В случае CosmosDB SDK построен поверх LINQ to SQL.

Используя IEnumerable, вы загружаете все данные из Cosmos в память, а затем применяете ваши фильтры инумерацией страниц.Это нормально, пока у вас есть только несколько записей, но по мере роста вашего набора данных вы, вероятно, можете представить, как это может стать серьезной проблемой.Обычно вы хотите выполнить как можно больше работы с базой данных и вернуть только минимальное количество результатов.Это намного перевесит любые соображения производительности при использовании отражения для построения ваших предикатов.

Одна из лучших частей IQueryable заключается в том, что он наследуется от IEnumerable, поэтому все, что работает с IEnumerable, будет работатьс IQueriable.Все, что вам нужно сделать, это избавиться от AsEnumerable().


Важно отметить, что не все операторы LINQ поддерживаются автоматически, и как только система нажмет один, она не сможетперевести это выполняет то, что он имеет до сих пор и делает все остальное в памяти.В документации есть список доступных операторов для CosmosDB.

Для вашего запроса будет большой, в то время как ваше предложение Where() поддерживается, Skip()и Take() в настоящее время нет.Это означает, что каждый раз, когда этот метод выполняется, все результаты будут возвращаться из CosmosDB, а затем будет оцениваться нумерация страниц.

В SDK есть несколько способов справиться с нумерацией страниц.Поддерживаемый в настоящее время способ - установить MaxItemCount внутри вашего FeedOptions на размер вашей страницы.Вместо функции Skip() текущая система использует токены продолжения.Для доступа к токену вы можете использовать AsDocumentQuery().Поскольку вам нужно переходить от каждой страницы к следующей, кэширование токенов может быть очень полезным - прыгать вокруг очень сложно.

Второй вариант - использовать v3 .Net SDK.Это в настоящее время в предварительном просмотре, но доступно на Nuget .Несколько месяцев назад skip / take было включено .В этом случае все до тех пор, пока вы не вызовете ToList(), должно быть переведено в SQL и оценено в CosmosDB.

...