Тормоза с запросом
От: merge  
Дата: 06.11.23 13:46
Оценка:
Есть вот такой код вполне обычный.
Стал метод сильно тормозить. Смотрю в профайлере и вижу что идет запрос без условий, а просто

SELECT * FROM Cars


и потом получается накладывается фильтр.
То есть причина тормоза понятна. Непонятно, почему фильтр не накладывается? я думал, что до ToList\ToArray запрос собирается для базы.

  код
        class DbContext
        {
      DbSet<Car> Cars { get; set; }
        }
        
somefunction()
       {
       Client client = GetClient();

        bool CarSelectorDatabase(Car car) => client.Type switch
        {
            Type.Common => CarSelectorCommon(car),
            Type.Legal => CarSelectorLegal(car),
            _ => throw new ArgumentOutOfRangeException()
        };     

        bool CarSelectorCommon(Car car) => client.IsVip()
            ? car.Country == client.Country &&        
              car.Model == client.Model &&
              car.SomeProperty = Client.SomeProperty
            : car.Country == client.Country &&
              car.Model == client.Model;

        bool CarSelectorLegal(Car car) =>
            car.Country == client.Country &&
            car.Model == client.Model && 
            car.LegalZip == Client.LegalZip;


        var realCar = dbContext.Cars.FirstOrDefault(CarSelectorDatabase).ToList();
      }


И что самое интересное если сделать простой switch то работает всё шустро и в разы быстрее.
то есть просто такой код делаем

 var realCar = client.Type switch
    {
       Common => dbContext.Cars.FirstOrDefault(car => car.Country == client.Country &&        
              car.Model == client.Model &&
              car.SomeProperty = Client.SomeProperty),
           Legal => ...
        }
Отредактировано 06.11.2023 13:56 merge . Предыдущая версия .
Re: Тормоза с запросом
От: Darky Darkov Россия  
Дата: 06.11.23 14:28
Оценка:
Здравствуйте, merge, Вы писали:

M>Есть вот такой код вполне обычный.

M>Стал метод сильно тормозить. Смотрю в профайлере и вижу что идет запрос без условий, а просто
M>

M>SELECT * FROM Cars


M>и потом получается накладывается фильтр.

M>То есть причина тормоза понятна. Непонятно, почему фильтр не накладывается? я думал, что до ToList\ToArray запрос собирается для базы.
M>...
M>И что самое интересное если сделать простой switch то работает всё шустро и в разы быстрее.
M>то есть просто такой код делаем


Мне кажется, что вы не используете IQueriable в функциях CarSelectorCommon и CarSelectorLegal. Надо как-то так:
Отредактировано 07.11.2023 14:27 VladD2 . Предыдущая версия .
Re: Тормоза с запросом
От: Darky Darkov Россия  
Дата: 06.11.23 14:30
Оценка:
Здравствуйте, merge!

M>Стал метод сильно тормозить. Смотрю в профайлере и вижу что идет запрос без условий, а просто

M>...
M>И что самое интересное если сделать простой switch то работает всё шустро и в разы быстрее.
M>то есть просто такой код делаем

Мне кажется, что вы не используете IQueriable в функциях CarSelectorCommon и CarSelectorLegal. Надо сделать так, чтобы они возвращали не bool, а IQueriable<Car>. Тогда SQL-запрос будет формироваться правильно.
Отредактировано 07.11.2023 14:29 VladD2 . Предыдущая версия .
Re: Тормоза с запросом
От: 尿컙拋㕪⬎⤇Ǥ꧃푙刾ꄔ൒  
Дата: 06.11.23 14:32
Оценка:
"собирается" ты имеешь ввиду DbLinq в состоянии протранслировать любую row-level логику в where сиквела запрос — я правильно понял?
Re: Тормоза с запросом
От: RushDevion Россия  
Дата: 06.11.23 14:59
Оценка: +1
  // Здесь, скорей всего, используется перегрузка от linq2objects Func<T, bool>, а не linq2sql Expression<Func<T, bool>>
  var realCar = dbContext.Cars.FirstOrDefault(CarSelectorDatabase);

  // Перепиши по человечески:
  var query = db.Cars.AsQueryable();
  query = client.Type swith {
    Common  => query.Where(x=>...),
    Legal => query.Where(x=>...),
        _ => throw NotSupported()     
  }
  return query.FirstOrDefault().ToList(); // Зачем тут, кстати, ToList(), если результат всегда один? Но это уже другой вопрос.
Re: Тормоза с запросом
От: gandjustas Россия http://blog.gandjustas.ru/
Дата: 06.11.23 15:48
Оценка: +1
Здравствуйте, merge, Вы писали:

M>То есть причина тормоза понятна. Непонятно, почему фильтр не накладывается?

А почему он должен накладываться, если ты передаешь в FirstOrDefault делегат типа Func<Car,bool> ? Для IQueryable такой перегрузки FirstOrDefaultне существует. Если ты наведешь мышкой в последней строке, то выяснишь какой метод вызывается.

M>я думал, что до ToList\ToArray запрос собирается для базы.

Для IQueryable<T> так и есть.
Re[2]: Тормоза с запросом
От: 尿컙拋㕪⬎⤇Ǥ꧃푙刾ꄔ൒  
Дата: 06.11.23 18:42
Оценка:
в чем мотивация? — запрос Select ... Where плюс минус сколько бы записей он не возвращал одну at most или несколько.
Re: Тормоза с запросом
От: Разраб  
Дата: 06.11.23 23:57
Оценка:
Здравствуйте, merge, Вы писали:
M>И что самое интересное если сделать простой switch то работает всё шустро и в разы быстрее.
что значит шустро? выводите на GUI? Как меряете?
☭ ✊ В мире нет ничего, кроме движущейся материи.
Re: Тормоза с запросом
От: Sinclair Россия https://github.com/evilguest/
Дата: 07.11.23 10:13
Оценка: +1
Здравствуйте, merge, Вы писали:
M>и потом получается накладывается фильтр.
M>То есть причина тормоза понятна. Непонятно, почему фильтр не накладывается? я думал, что до ToList\ToArray запрос собирается для базы.
Всё немного сложнее. Запрос начинает выполняться в тот момент, когда вы пытаетесь обратиться к нему как к IEnumerable<T>.
ToList() и ToArray() — просто два удобных способа сделать именно это.
Для того, чтобы продолжать конструирование запроса, вам нужно передавать в Where (или FirstOrDefault) не Func<T, bool>, а Expression<Func<T, bool>>.
То есть, ваш код можно было бы починить примерно так:
public Car somefunction()
{
  Client client = GetClient();
  Expression<Func<Car, bool>> carSelectorCommon = (Car car) => client.IsVip()
            ? car.Country == client.Country &&        
              car.Model == client.Model &&
              car.SomeProperty = Client.SomeProperty
            : car.Country == client.Country &&
              car.Model == client.Model;

  Expression<Func<Car, bool> carSelectorLegal = (Car car) =>
            car.Country == client.Country &&
            car.Model == client.Model && 
            car.LegalZip == Client.LegalZip;

  var carSelectorDatabase => client.Type switch
  {
    Type.Common => carSelectorCommon,
    Type.Legal => carSelectorLegal,
    _ => throw new ArgumentOutOfRangeException()
  };     

  return dbContext.Cars.FirstOrDefault(carSelectorDatabase);
}
Уйдемте отсюда, Румата! У вас слишком богатые погреба.
Re[2]: Тормоза с запросом
От: IT Россия linq2db.com
Дата: 08.11.23 01:21
Оценка: 80 (1)
Здравствуйте, Sinclair, Вы писали:

S>То есть, ваш код можно было бы починить примерно так:


Можно проще и без заморочек с типами:

public Car somefunction()
{
    var client = GetClient();
    var cars   = dbContext.Cars;

    cars = (client.Type, client.IsVip()) switch
    {
        (Type.Common, true)  => cars.Where(car =>
            car.Country == client.Country &&        
            car.Model   == client.Model &&
            car.SomeProperty == Client.SomeProperty),

        (Type.Common, false) => cars.Where(car =>
            car.Country == client.Country &&
            car.Model   == client.Model),

        (Type.Legal, _)      => cars.Where(car =>
            car.Country  == client.Country &&
            car.Model    == client.Model && 
            car.LegalZip == Client.LegalZip),

        _ => throw new ArgumentOutOfRangeException()
    }

    return cars.FirstOrDefault()
}


А если не предполагается больше никаких фильтров, то ещё короче:

public Car somefunction()
{
    var client = GetClient();
    var cars   = dbContext.Cars.Where(car =>
        car.Country == client.Country &&
        car.Model   == client.Model);

    cars = (client.Type, client.IsVip()) switch
    {
        (Type.Common, true)  => cars.Where(car => car.SomeProperty == Client.SomeProperty),
        (Type.Common, false) => cars,
        (Type.Legal, _)      => cars.Where(car => car.LegalZip == Client.LegalZip),
        _ => throw new ArgumentOutOfRangeException()
    }

    return cars.FirstOrDefault()
}
Если нам не помогут, то мы тоже никого не пощадим.
Отредактировано 08.11.2023 3:00 IT . Предыдущая версия .
Re: Тормоза с запросом
От: Разраб  
Дата: 08.11.23 01:32
Оценка:
Здравствуйте, merge, Вы писали:


M>
  код
M>
M>        class DbContext
M>        {
M>      DbSet<Car> Cars { get; set; }
M>        }



M>        var realCar = dbContext.Cars.FirstOrDefault(CarSelectorDatabase).ToList();
M>      }
M>



Откуда у Car взялся метод ToList()?
☭ ✊ В мире нет ничего, кроме движущейся материи.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.