Скомпилированные запросы

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

Функциональность скомпилированных запросов позволяют приложению кэшировать запрос в форму, понятную для источника данных. Поэтому достаточно один раз создать такой запрос и затем выполнять его много раз, что повышает производительность приложения.

Хотя EF Core может автоматически компилировать и кэшировать запросы на основании хэш-представления выражений LINQ, тем не менее компилируемые запросы позволяют хоть немножно но повысить производительность, так как в этом случае не вычисляется хэш-значение и не производится поиск в кэше, а приложение может использовать уже скомпилированные запросы через вызов делегатов.

Для компиляции запроса набор выражений LINQ передается в метод EF.CompileQuery(), а скомпилированный запрос затем присваивается делегату:

делегат = EF.CompileQuery((параметры) => выражения_LINQ)

Например, возьмем следующие сущности:

using Microsoft.EntityFrameworkCore;

public class Company
{
    public int Id { get; set; }
    public string? Name { get; set; }
    public List<User> Users { get; set; } = new();
}

public class User
{
    public int Id { get; set; }
    public string? Name { get; set; }
    public int Age { get; set; }
    public int CompanyId { get; set; }
    public Company? Company { get; set; }
}
public class ApplicationContext : DbContext
{
    public DbSet<User> Users { get; set; } = null!; 
    public DbSet<Company> Companies { get; set; } = null!;

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlite("Data Source=helloapp.db");
    }
}

Создадим и используем пару скомпилированных запросов.

using Microsoft.EntityFrameworkCore;

Func<ApplicationContext, int, User?> userById =
            EF.CompileQuery((ApplicationContext db, int id) =>
                    db.Users.Include(c => c.Company).FirstOrDefault(c => c.Id == id));

 Func<ApplicationContext, string, int, IEnumerable<User>> usersByNameAndAge =
    EF.CompileQuery((ApplicationContext db, string name, int age) =>
            db.Users.Include(c => c.Company)
                    .Where(u => EF.Functions.Like(u.Name!, name) && u.Age > age));

// добавляем данные для тестирования
using (ApplicationContext db = new ApplicationContext())
{
    // пересоздаем базу данных
    db.Database.EnsureDeleted();
    db.Database.EnsureCreated();

    Company microsoft = new Company { Name = "Microsoft" };
    Company google = new Company { Name = "Google" };
    db.Companies.AddRange(microsoft, google);

    User tom = new User { Name = "Tom", Age = 36, Company = microsoft };
    User bob = new User { Name = "Bob", Age = 39, Company = google };
    User alice = new User { Name = "Alice", Age = 28, Company = microsoft };
    User kate = new User { Name = "Kate", Age = 25, Company = google };
    User tomas = new User { Name = "Tomas", Age = 30, Company = microsoft };
    User tomek = new User { Name = "Tomek", Age = 42, Company = google };

    db.Users.AddRange(tom, bob, alice, kate, tomas, tomek);
    db.SaveChanges();
}
using (ApplicationContext db = new ApplicationContext())
{
    var user = userById(db, 1);
    if(user!=null) Console.WriteLine($"{user.Name} - {user.Age} \n");

    var users = usersByNameAndAge(db, "%Tom%", 30).ToList();
    foreach (var u in users)
        Console.WriteLine($"{u.Name} - {u.Age}");
}

Здесь определены два делегата, которые фактически представляют два запроса - userById (ищет пользователя по id) и usersByNameAndAge (ищет пользователей по имени и возрасту).

Делегат userById принимает два входных параметра - контекст данных и id объекта и выходной параметр - найденный объект User. В метод EF.CompileQuery для компиляции запроса передается лямбда-выражение, параметры которого - это входные параметры делегата, а тело которого - собственно набор выражений LINQ:

Func<ApplicationContext, int, User?> userById =
	EF.CompileQuery((ApplicationContext db, int id) =>
         db.Users.Include(c => c.Company).FirstOrDefault(c => c.Id == id));

Делегат usersByNameAndAge принимает три входных параметра - контекст данных, часть имя пользователя и его возраст. Выходной параметр - набор объектов в виде типа IEnumerable<User>. Этот делегат также передает в EF.CompileQuery свои входные параметры и получает некоторый результат:

Func<ApplicationContext, string, int, IEnumerable<User>> usersByNameAndAge =
	EF.CompileQuery((ApplicationContext db, string name, int age) =>
		db.Users.Include(c => c.Company)
				 .Where(u=>EF.Functions.Like(u.Name!, name) && u.Age>age));

Затем через данные делегаты мы сможем многократно выполнять соответствующие запросы:

if(user!=null) Console.WriteLine($"{user.Name} - {user.Age} \n");
var users = usersByNameAndAge(db, "%Tom%", 30).ToList();

В итоге в данном случае мы получим следующий консольный вывод:

Tom - 36

Tom - 36
Tomek - 42
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850