Фильтры запросов уровня модели (Model-level query filters) позволяют определить предикат запроса LINQ непосредственно в метаданных модели (обычно в методе OnModelCreating контекста данных). Такие фильтры автоматически применяются к любым запросам LINQ, в которых используются классы, для которых определен фильтр.
Пусть у нас определены следующие классы:
public class User { public int Id { get; set; } public string? Name { get; set; } public int Age { get; set; } public int RoleId { get; set; } public Role? Role { get; set; } } public class Role { public int Id { get; set; } public string? Name { get; set; } }
Здесь класс пользователя имеет ссылку на класс роли, к которой он принадлежит. И также определим контекст данных:
using Microsoft.EntityFrameworkCore; public class ApplicationContext : DbContext { public DbSet<User> Users { get; set; } = null!; public DbSet<Role> Roles { get; set; } = null!; public int RoleId { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlite("Data Source=helloapp.db"); } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<User>().HasQueryFilter(u => u.Age > 17 && u.RoleId == RoleId); } }
В метод HasQueryFilter() передается предикат, которому должен удовлетворять объект User, чтобы быть извлеченным из базы данных. То есть в результате запросов будут извлекаться только те объекты User, у которых значение свойства Age больше 17, а свойство RoleId равно значению свойства RoleId их контекста данных.
Используем данные классы:
using (ApplicationContext db = new ApplicationContext()) { // пересоздадим базу данных db.Database.EnsureDeleted(); db.Database.EnsureCreated(); Role adminRole = new Role { Name = "admin" }; Role userRole = new Role { Name = "user" }; User user1 = new User { Name = "Tom", Age = 17, Role = userRole }; User user2 = new User { Name = "Bob", Age = 18, Role = userRole }; User user3 = new User { Name = "Alice", Age = 19, Role = adminRole }; User user4 = new User { Name = "Sam", Age = 20, Role = adminRole }; db.Roles.AddRange(userRole, adminRole); db.Users.AddRange(user1, user2, user3, user4); db.SaveChanges(); } using (ApplicationContext db = new ApplicationContext() { RoleId = 2 }) { var users = db.Users.Include(u => u.Role).ToList(); foreach (User user in users) Console.WriteLine($"Name: {user.Name} Age: {user.Age} Role: {user.Role?.Name}"); }
Результатом будет следующий консольный вывод:
Name: Alice Age:19 Role:admin Name: Sam Age:20 Role:admin
То есть только два объекта из добавленных четырех соответствуют тому предикату, который был передан в HasQueryFilter. И данный фильтр будет действовать для всех запросов к базе данных, которые извлекают данные из таблицы Users. Например, нахождение минимального возраста:
using (ApplicationContext db = new ApplicationContext() { RoleId = 2 }) { int minAge = db.Users.Min(x => x.Age); Console.WriteLine(minAge); // 19 }
Несмотря на то, что минимальный возраст по всем 4 объектам составляет 17, но так как действует фильтр, при запросе будут учитываться только те объекты в бд, которые соответствуют фильтру.
Если необходимо во время запроса отключить фильтр, то применяется метод IgnoreQueryFilters():
using (ApplicationContext db = new ApplicationContext() { RoleId = 2 }) { int minAge = db.Users.IgnoreQueryFilters().Min(x => x.Age); Console.WriteLine(minAge); // 17 }