LINQ содержит и исправляет

#entity-framework #linq

#entity-framework #linq

Вопрос:

У меня есть запрос LINQ

 var age = new int[]{1,2,3};
dbContext.TA.WHERE(x=> age.Contains( x.age)).ToList()
  

В онлайн-статье # 11 (https://medium.com/swlh/entity-framework-common-performance-mistakes-cdb8861cf0e7 ) упомянул, что это не очень хорошая практика, поскольку она создает много планов выполнения на сервере SQL.

В этом случае, как следует пересмотреть LINQ, чтобы я мог делать то же самое, но минимизировать количество сгенерированных планов выполнения?

(обратите внимание, что я не собираюсь преобразовывать его в хранимую процедуру и передавать amp; join с UDT, поскольку для этого снова требуется слишком много усилий)

Ответ №1:

В этой статье предлагаются некоторые полезные вещи, которые следует иметь в виду при написании выражений для EF. Как правило, этот пример следует иметь в виду, а не жесткое правило типа «никогда не делай этого». Это предупреждение о написании запросов, которые допускают множественный выбор, и о том, чтобы избегать этого, когда это возможно, поскольку это будет дороже.

В вашем примере с чем-то вроде «Ages» наличие жестко запрограммированного списка значений не вызывает проблем, потому что каждое выполнение использует один и тот же список. (до тех пор, пока приложение не будет повторно скомпилировано с новым списком, или у вас есть код, который по какой-либо причине изменяет список.) Примеры, в которых это может быть совершенно допустимо использовать, — это что-то вроде статусов, где у вас есть перечисление статуса. Если существует небольшое количество допустимых статусов, которые может иметь запись, то объявление общего массива допустимых статусов для использования в Contains предложении — это нормально:

 public void DeleteEnquiry(int enquiryId)
{
    var allowedStatuses = new[] { Statuses.Pending, Statuses.InProgress, Statuses.UnderReview };

    var enquiry = context.Enquiries
        .Where(x => x.EnquiryId == enquiryId amp;amp; allowedStatuses.Contains(x.Status))
        .SingleOrDefault();
    
    try
    {
        if(enquiry != null)
        {
            enquiry.IsActive = false;
            context.SaveChanges();
        }
        else
        {
            // Enquiry not found or invalid status.
        }
    } 
    catch (Exception ex) { /* handle exception */ }
}
  

Статусы в списке не изменятся, поэтому план выполнения является статичным для этого контекста.

Проблема в том, что вы принимаете что-то вроде параметра с критериями, которые включают список для Contains предложения.

маловероятно, что кто-то захочет загрузить данные, в которых пользователь мог бы выбрать возраст «2, 4 и 6», но скорее они захотят выбрать что-то вроде: «>= 2», или «<=6, или «2>= 6», поэтому вместо создания метода, который принимает список допустимых возрастов:

 public IEnumerable<Children> GetByAges(int[] ages)
{
    return _dbContext.Children.Where(x => ages.Contains( x.Age)).ToList();
}
  

Вероятно, вам было бы лучше использовать ранжирование параметров:

 private IEnumerable<Children> GetByAgeRange(int? minAge = null, int? maxAge = null)
{
    var query = _dbContext.Children.AsQueryable();
    if (minAge.HasValue)
        query = query.Where(x => x.Age >= minAge.Value);
    if (maxAge.HasValue)
        query = query.Where(x => x.Age <= maxAge.Value);
    return query.ToList();
}

private IEnumerable<Children> GetByAge(int age)
{
    return _dbContext.Children.Where(x => x.Age == age).ToList();
}
  

Комментарии:

1. Привет, спасибо за ответ и оценку BI. Но дело в том, что возраст — это всего лишь пример. В моем сценарии массив не является статичным и меняется очень часто / драматично (размер набора данных составляет от 0 до нескольких тысяч). Я почти уверен, что arr.Contains(x=>x.value) соответствует статье «Не» .

2. Если вы ищете альтернативу, обновите свой вопрос примером, который точно демонстрирует, чего вы хотите достичь. Если вы заполняете массив на основе выбора пользователя, то вы, вероятно, в значительной степени зависли от стоимости плана выполнения. Если выбор может быть выполнен вместо этого на основе объединения с другим столбцом / критерием, то этого можно было бы избежать. Вы столкнулись с реальной проблемой производительности с вашим существующим запросом или просто беспокоитесь о возможном возникновении такой проблемы в будущем?