Данное руководство устарело. Актуальное руководство: Руководство по Entity Framework Core 7
Скомпилированные запросы позволяют приложению кэшировать перевод запроса в форму, понятную для источника данных. Поэтому достаточно один раз создать такой запрос и затем выполнять его много раз, что повышает производительность приложения.
Хотя EF Core может автоматически компилировать и кэшировать запросы на основании хэш-представления выражений LINQ, тем не менее компилируемые запросы позволяют хоть немножно но повысить производительность, так как в этом случае не вычисляется хэш-значение и не производится поиск в кэше, а приложение может использовать уже скомпилированные запросы через вызов делегатов.
Для компиляции запроса набор выражений LINQ передается в метод EF.CompileQuery(), а скомпилированный запрос затем присваивается делегату:
делегат = EF.CompileQuery((параметры) => выражения_LINQ)
Например, возьмем следующие модели:
public class Company { public int Id { get; set; } public string Name { get; set; } public List<User> Users { get; set; } = new List<User>(); } 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<Company> Companies { get; set; } public DbSet<User> Users { get; set; } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=helloappdb;Trusted_Connection=True;"); } }
Создадим и используем пару скомпилированных запросов.
using Microsoft.EntityFrameworkCore; using System; using System.Collections.Generic; using System.Linq; namespace HelloApp { public class Program { private static Func<ApplicationContext, int, User> userById = EF.CompileQuery((ApplicationContext db, int id) => db.Users.Include(c => c.Company).FirstOrDefault(c => c.Id == id)); private static 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)); public static void Main(string[] args) { // добавляем данные для тестирования 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); 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:
private static 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 свои входные параметры и получает некоторый результат:
private static 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));
Затем через данные делегаты мы сможем многократно выполнять соответствующие запросы:
var user = userById(db, 1); var users = usersByNameAndAge(db, "%Tom%", 30).ToList();
В итоге в данном случае мы получим следующий консольный вывод:
Tom - 36 Tom - 36 Tomek - 42