Комментарии 16
command.CommandText.Contains("-- FOR UPDATE", StringComparison.InvariantCulture)
Избыточно сравнивать с локалью
https://habr.com/en/companies/skbkontur/articles/854340/
В данном случае - однозначно. Скорее как устоявшаяся привычка.
Всмысле, если убрать 2-й параметр у Contains, будет ещё хуже (используется локаль от текущего потока). А надо OrdinalIgroreCase
Запросы тюнить лучше в нативном SQL. Иначе двойная работа, если перепроверять запрос из ЕF.
Зависит от. Понятно, что если структура базы такая, что нагенерированный EF код надо постоянно перепроверять, то ну его нафиг. Тут, да, никакие интерсепторы не помогут - только RawSql, а если кривых запросов ещё и достаточно солидный процент от общего числа, то стоит в принципе подумать на тему отказа от EF.
Но сделать интерсептор для того же For Update вместо переписывания в 3-х местах кода на нативный SQL - вполне себе годное решение, как мне кажется. И да, я полностью согласен, что хранимка в данном случае работала бы немного быстрее. Нюанс в том, что при ожидаемой частоте апдейтов потратили бы больше времени разработчика на переписывание, чем сэкономили бы машинного времени при такой оптимизации.
Хорошая статья. Коротко, без разглагольствований и лишних отступлений. И, реальный, а не высосанный из пальца пример. По больше бы таких статей. Плюсанул.
Выглядит как очень хрупкий солюшен, потому что неизвестно, что нагенерит еф.
Применять FOR UPDATE на какой-нибудь сложный кросс-джойн по нескольким таблицам с агрегаторами и группировкой - не надо так. Он там просто не сработает, причём EF там будет ни при чём. Ну а что нагенерирует EF на более-менее типовой селект по одной таблице с WHERE в качестве условия - вариантов там не особо много.
Плюс сам по себе механизм интерсепторов подразумевает наличие некоторых скилов в EF и базе у человека, который пишет сам интерсептор. А дальнейшее использование ничем не отличается от каких-нибудь агрегаторов. Их точно также не везде можно использовать, и можно ошибку словить, если сделаешь это криво.
Я и не говорю что надо использовать какие-то сложные запросы, смысл в том, что еф не предсказуем в генерации даже казалось бы простых вещей, поэтому вставка чего-либо просто в конец выражения рисковое дело.
А можно, пожалуйста, практический пример того, как более-менее современная версия EF одно выражение Where трансформирует во что-то кроме SELECT … FROM … WHERE …? Спрашиваю без малейшего стёба, так как я такой риск сознательно откидывал как несущественный.
Я прекрасно понимаю, как можно «положить» этот запрос, но честно смог придумать только четыре сценария:
Применение ForUpdate к сложносоставному запросу, к которому в принципе нельзя применять эту команду на уровне SQL. Ну как бы сам себе злобный Буратино, не надо отвёрткой гвозди забивать.
В будущих версиях EF решат сломать обратную совместимость при генерации запросов и, например, начать явно прописывать ; в конце. Ок, это будет не первый на моей памяти случай, когда внешняя библиотека что-то ломает. Заранее это не предскажешь, так как могут и две ; подряд поставить, например. Тут, главное, при подъёме версий библиотек автотесты не забывать гонять.
Смена базы данных, при которой надо вместо FOR UPDATE начинать писать совсем другой код и по другим правилам. Тут тоже заранее хз, что делать, так как смена базы дело достаточно капитальное и не зная целевую базу сложно универсальный код написать. Тем более, что тут любой raw sql или вызов хранимую отломится точно также.
Другой интерсептор, написанный без учёта совместимости с уже имеющимися. Например, добавляющий в конец SELECT запроса ; по принципу «а для красоты». Такое только на ревью отслеживать, плюс, опять же, автотесты.
Старые версии могли начать генерировать проекции (селект фром селект фром ...), если в условии использовалась какая-то специфическая функция. Точно такая же проблема вылезла и в еф коре когда они добавили полнотекстовый поиск, на проекциях он не работает (в SQL Server), соотв. когда такая проекция появлялась - он падал, очевидно, появлялась она не всегда. То есть даже если вам удастся вписать в нужное место выражение, форма запроса все равно может сломать. Если бы я реализовывал полнотекстовый поиск сбоку мне надо было бы как-то сказать еф - не выноси условие, а таких механизмов нет. Само условие может быть не простым, там ведь не только а=б может быть, а может оказаться подзапрос с аггрегацией и тд.
Понять что еще он может сделать, можно только изучая исходный код, а до тех пор это просто черный ящик.
Кажется, что ничего из перечисленного не подпадает под определение "более-менее современная версия EF одно выражение Where трансформирует во что-то кроме SELECT … FROM … WHERE … ". Но это, в любом случае, думаю, не принципиально.
Сам по себе риск однозначно есть, вы абсолютно правы. На сколько он большой - вопрос дискуссионный. Мне кажется, что преимущества перевешивают риски, вам - что нет.
В любом случае, инструмент есть, а пользоваться им, или нет, уже пусть читатели сами решают.
Почему-то в первую очередь был реализован синхронный метод ReaderExecuting
, хотя сейчас многие пользуется асинхронными для работы с БД и текущий пример не работает сходу, было бы правильней переопределить одновременно ReaderExecuting и ReaderExecutingAsync
Тюним запросы в EF Core с помощью интерсепторов