TGStat
TGStat
Type to search
Advanced channel search
  • flag English
    Site language
    flag Russian flag English flag Uzbek
  • Sign In
  • Catalog
    Channels and groups catalog Regional compilations Thematic compilations Платные каналы Search for channels
    Add a channel/group
  • Ratings
    Rating of channels Rating of groups Posts rating
    Ratings of brands and people
  • Analytics
  • Search by posts
  • Telegram monitoring
  • Promotion
    Advertising through Yandex Business Advertising in channels through TGStat Agency Advertising on TGStat.ru website
.NET sh blog

22 Feb 2025, 12:32

Open in Telegram Share Report

EntityFrameworkCore потрясающий инструмент который сильно упрощает нам жизнь, но если не контролировать какие SQL запросы уходят в СУБД, то это может доставить боль при обновлениях мажорных версий, т.к. EF может в целом изменить подходы к построению SQL и что-нибудь сломать или сильно замедлить запросы из-за генерации неэффективных запросов.

А такое случается достаточно часто, несмотря даже на сумасшедшие 286 882 тестов, которые к сожалению всё равно не могут покрыть всевозможные сценарии и так или иначе будут просачиваться баги.

К примеру, несколько лет назад на одном проекте при апдейте с версии 2.2 на 3.0, у меня один запрос стал тянуть чуть ли не все данные из СУБД, из-за чего ему стало не хватать work_mem, поэтому он начал активно использовать диск и генерировал временные файлы. Но запрос выполнялся долго, отменялся по таймауту, но почему-то не подчищалась папка temp и таким образом диск быстро заполнялся и пришлось ребутать СУБД чтобы он не ушел в режим readonly.
Решилось всё переписыванием запроса с учетом особенностей уже версии 3.0.

Тогда же занесли еще один досадный баг, когда просто забыли рекурсивно пройтись по деревьям выражений из-за чего сломалось использование PredicateBuilder, а сам фикс был однострочным, но помню много времени ждал пока этот фикс доедет до релиза.
А из свежих, в 9 версии сломали GroupBy в некоторых кейсах.

Подобные проблемы регулярно встречаются и хорошо бы это покрыть тестами и недопустить проблемы до прода.

Самый простой способ тестирования генератора SQL запросов - это snapshot тесты.
В .NET есть крутая библиотека Verify и Verify.EntityFramework.
Используя его, мы можем встроиться в DbContext и с помощью интерцепторов записывать все исходящие SQL запросы и сохранить их как снепшот.
При апдейте версии EFCore или при изменении нашего кода, такие тесты покажут изменения в SQL запросах если они были.

Полноценного примера в доке нет, поэтому держите гайд:

1. В Composition Root (в проекте со Startup.cs или Program.cs) добавить зависимость от Verify.EntityFramework и вызвать метод расширения EnableRecording на DbContextOptionsBuilder. Его надо добавить только при запуске тестов:
builder.Services.AddDbContext((sp, options) =>
{
// тут настройка DbContext: options.UseNpgsql и т.п.

// при запуске интеграционных тестов окружение проставляю в IntegrationTests.
if (_env.EnvironmentName == "IntegrationTests" && options is DbContextOptionsBuilder o)
{
o.EnableRecording();
}
}

2. В сборке с интеграционными тестами подключить библиотеку Verify.NUnit/XUnit и инициализировать Verify.EntityFramework:
public static class ConfigureVerify
{
[ModuleInitializer]
public static void Init()
{
var model = GetDbModel();
VerifyEntityFramework.Initialize(model);
}

private static IModel GetDbModel()
{
var options = new DbContextOptionsBuilder();
options
.UseNpgsql("fake")
.UseSnakeCaseNamingConvention();
using var data = new ApplicationDbContext(options.Options);
return data.Model;
}
}

3. В тестах обернуть в using вызов тестируемого кода (всю секцию act)
using (Recording.Start())
{
// act
await sut.ExecuteAsync();
}

и вызывать Verify в качестве assert:
await Verify().AutoVerify(includeBuildServer: false);

После успешного прохода теста, появится *.verified.txt файл, в котором будут все отправленные SQL запросы в рамках тестируемого сценария:
{
ef: [
{
Type: ReaderExecutedAsync,
HasTransaction: true,
Text:
-- StrictOrderingOutboxRepository:LockAndReturnItemsBatchAsync

SELECT * FROM kafka_ef_outbox_items

ORDER BY id ASC
LIMIT 10
FOR UPDATE NOWAIT;
},
]
}
Если изменить код, добавить еще SQL вызовы или поменять их местами, то изменится и снепшот, который можно будет провалидировать и закоммитить в git репозиторий.
Если изменится SQL запрос, можно всегда его отсюда скопировать и сделать explain, посмотреть на план запроса и либо переписать либо оставить как есть.

566 0 10 1 23
Catalog
Channels and groups catalog Channels compilations Search for channels Add a channel/group
Ratings
Rating of Telegram channels Rating of Telegram groups Posts rating Ratings of brands and people
API
API statistics Search API of posts API Callback
Our channels
@TGStat @TGStat_Chat @telepulse @TGStatAPI
Read
Академия TGStat Telegram Research 2019 Telegram Research 2021 Telegram Research 2023
Contacts
Справочный центр Support Email Jobs
Miscellaneous
Terms and conditions Privacy policy Public offer
Our bots
@TGStat_Bot @SearcheeBot @TGAlertsBot @tg_analytics_bot @TGStatChatBot