Фильтры запросов уровня модели

Данное руководство устарело. Актуальное руководство: Руководство по Entity Framework Core 7

Последнее обновление: 18.11.2020

Фильтры запросов уровня модели (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; }
}

Здесь класс пользователя имеет ссылку на класс роли, к которой он принадлежит. И также определим контекст данных:

public class ApplicationContext : DbContext
{
	public DbSet<User> Users { get; set; }
	public DbSet<Role> Roles { get; set; }

	public int RoleId { get; set; }
	
	protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
	{
		optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=helloappdb;Trusted_Connection=True;");
	}

	protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
		modelBuilder.Entity<User>().HasQueryFilter(u => u.Age > 17 && u.RoleId == this.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
}
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850