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

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

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

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

Хотя 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
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850