5/5 - (1 vote)

У цьому посібнику ми детально розберемо одну з найкорисніших функцій Entity Framework Core (EF Core) — глобальні фільтри запитів (Global Query Filters).

Якщо вам коли-небудь доводилося писати однакову умову WHERE у кожному запиті, ви знаєте, як легко забути додати її хоча б один раз — і це може призвести до помилки або навіть випадкового розкриття конфіденційних даних. Глобальні фільтри запитів вирішують цю проблему, автоматично застосовуючи визначені умови до всіх запитів у застосунку.

У цій статті ми розберемо, що таке глобальні фільтри, коли їх варто використовувати, як їх реалізувати та які покращення з’явилися в EF Core 10 завдяки появі іменованих фільтрів (Named Filters). Також ми створимо практичний приклад реалізації на .NET 10 із використанням SQLite.

Що таке глобальні фільтри запитів

Простіше кажучи, глобальний фільтр запиту — це умова, яку EF Core автоматично застосовує до всіх запитів для певної сутності. Це можна вважати постійною умовою WHERE, визначеною один раз у моделі та застосовуваною EF Core до кожної вибірки.

Це особливо зручно, коли потрібно, щоб певні правила діяли завжди. Найпоширеніші приклади:

  • М’яке видалення (soft delete) — приховування записів, позначених як видалені.
  • Багатоорендність (multi-tenancy) — ізоляція даних для різних клієнтів (тенантів).
  • Архівація — збереження старих записів без їх відображення у звичайних запитах.

Практичні приклади

1. М’яке видалення (Soft Delete)

Замість фізичного видалення рядків із бази можна позначати їх як видалені, встановлюючи прапорець IsDeleted = true. Глобальні фільтри автоматично виключають такі записи з усіх запитів.

2. Багатоорендність (Multi-Tenancy)

У SaaS-застосунках, де кілька компаній користуються однією базою даних, глобальний фільтр гарантує, що кожен клієнт бачить лише свої дані — без потреби вручну додавати фільтр у кожен запит.

3. Архівація

Деякі дані потрібно зберігати для аудиту чи звітності, але не відображати під час щоденних операцій. Глобальний фільтр дозволяє приховувати такі записи, зберігаючи можливість їх отримання при потребі.

Реалізація глобальних фільтрів у .NET 10

Розглянемо, як реалізувати м’яке видалення за допомогою глобальних фільтрів EF Core у новому проєкті .NET 10 Web API.

Крок 1: Налаштування проєкту

Створюємо новий проєкт і встановлюємо пакети EF Core:

  • Microsoft.EntityFrameworkCore
  • Microsoft.EntityFrameworkCore.Sqlite
  • Microsoft.EntityFrameworkCore.Tools

У цьому прикладі використовується база даних SQLite, яка дозволяє EF Core працювати з локальним файлом бази.

Крок 2: Створення сутності

У папці Entities створюємо клас Blog:

public sealed class Blog
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public bool IsDeleted { get; set; }
}/pre>
Прапорець IsDeleted використовується для позначення записів як видалених без фізичного видалення з бази.

Крок 3: Налаштування DbContext

У папці Data створюємо клас ApplicationDbContext, що успадковує DbContext. Додаємо властивість DbSet<Blog> і перевизначаємо метод OnModelCreating:
modelBuilder.Entity<Blog>()
.HasQueryFilter(b => !b.IsDeleted);

Тепер EF Core автоматично виключатиме м’яко видалені записи з усіх запитів до таблиці Blogs.

Крок 4: Налаштування API-ендпоїнтів

Створюємо кілька методів API:

  • GET /api/blogs — повертає всі блоги (крім видалених).
  • GET /api/blogs/all — повертає всі блоги, включно з видаленими (.IgnoreQueryFilters()).
  • GET /api/blogs/{id} — отримати блог за ID.
  • POST /api/blogs — створює новий блог.
  • DELETE /api/blogs/{id} — виконує м’яке видалення.

Крок 5: Підключення SQLite і DI

Додаємо рядок підключення у appsettings.json:

"ConnectionStrings": {
"DefaultConnection": "Data Source=Data/AppDb.db"
}

Реєструємо контекст у Program.cs:

builder.Services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlite(builder.Configuration.GetConnectionString("DefaultConnection")));

Крок 6: Створення міграцій

У консолі диспетчера пакетів виконуємо:

Add-Migration Initial
Update-Database

Буде створено базу даних SQLite і таблицю Blogs з полями Id, Name та IsDeleted.

Тестування API

Перевіряємо роботу через Postman або .http файл:

  • GET /api/blogs → порожній масив.
  • POST /api/blogs → створює блог.
  • GET /api/blogs → показує створений блог.
  • GET /api/blogs/all → ті ж дані (видалених ще немає).

Реалізація м’якого видалення

Змінюємо метод Delete, щоб він не видаляв записи фізично.

Перевизначаємо метод SaveChangesAsync:

public override Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
{
foreach (var entry in ChangeTracker.Entries<Blog>().Where(e => e.State == EntityState.Deleted))
{
entry.State = EntityState.Modified;
entry.Entity.IsDeleted = true;
}
return base.SaveChangesAsync(cancellationToken);
}
Тепер при видаленні запис лише позначається як видалений.

Покращення в EF Core 10: Іменовані фільтри

До версії EF Core 10 були обмеження:

  • Лише один фільтр на сутність.
  • .IgnoreQueryFilters() вимикав усі фільтри одразу.

Через це не можна було, наприклад, вимкнути фільтр м’якого видалення, зберігши фільтр ізоляції орендаря.

Іменовані фільтри вирішують цю проблему.

Тепер можна:

  • Визначати кілька фільтрів для однієї сутності.
  • Вимикати лише потрібний фільтр.

Приклад:

modelBuilder.Entity<Blog>()
.HasQueryFilter("SoftDeleteFilter", b => !b.IsDeleted);

Щоб вимкнути лише цей фільтр:

context.Blogs.IgnoreQueryFilters("SoftDeleteFilter");

Використання констант для імен фільтрів

Щоб уникнути помилок і підвищити читабельність, створіть статичний клас із константами:

public static class BlogFilters
{
public const string SoftDeleteFilter = "SoftDeleteFilter";
}

Тепер можна звертатися до фільтра так:

.IgnoreQueryFilters(BlogFilters.SoftDeleteFilter);

Фінальне тестування

Після додавання іменованих фільтрів:

  • GET /api/blogs → показує лише активні блоги.
  • POST /api/blogs → створює новий блог.
  • GET /api/blogs/all → показує всі блоги, включно з видаленими.

Висновок

Глобальні фільтри запитів — це потужний інструмент EF Core, який допомагає автоматизувати логіку вибірки даних і підвищує безпеку застосунку.

У цьому посібнику ми розібрали:

  • Що таке глобальні фільтри запитів.
  • Як реалізувати м’яке видалення.
  • Які покращення з’явилися в EF Core 10 з іменованими фільтрами.

Використовуючи ці фільтри, ви зможете уникнути дублювання коду, підвищити безпеку та зробити архітектуру застосунку чистішою й гнучкішою.