Данное руководство устарело. Актуальное руководство: Руководство по Entity Framework Core 7
Для взаимодействия с источником данных Entity Framework Core использует технологию LINQ to Entities. В основе данной технологии лежит язык интегрированных запросов LINQ (Language Integrated Query). LINQ предлагает простой и интуитивно понятный подход для получения данных с помощью выражений, которые по форме близки выражениям языка SQL.
Хотя при работе с базой данных мы оперируем запросами LINQ, но, к примеру, база данных MS SQL Server понимает только запросы на языке SQL. Поэтому Entity Framework Core, используя выражения LINQ to Entities, транслирует их в определенные запросы, понятные для используемого источника данных.
Создавать запросы мы можем двумя способами: через операторы LINQ и через методы расширения. Пусть для рассмотрения основных запросов в LINQ to Entities у нас будут следующие модели со связью один-ко-многим:
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;"); } }
Вначале используем некоторые операторы LINQ:
using System; using System.Linq; using Microsoft.EntityFrameworkCore; using System.Threading.Tasks; namespace HelloApp { public class Program { 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 }; db.Users.AddRange(tom, bob, alice, kate); db.SaveChanges(); } using (ApplicationContext db = new ApplicationContext()) { var users = (from user in db.Users.Include(p => p.Company) where user.CompanyId == 1 select user).ToList(); foreach (var user in users) Console.WriteLine($"{user.Name} ({user.Age}) - {user.Company.Name}"); } } } }
Вначале здесь происходит добавление данных, если они отсутствуют. Далее примяются Linq-операторы:
var users = (from user in db.Users.Include(p=>p.Company) where user.CompanyId == 1 select user).ToList();
Данный запрос говорит, что из набора db.Users
надо выбрать каждый объект в переменную user.
Далее с помощью оператора where
проводится фильтрация объектов, и если объект соответствует критерию
(в данном случае id компании должно равняться 1), то этот объект передается дальше. И в конце оператор select
передает выбранные
значения в результирующую выборку, которая возвращается LINQ-выражением.
В итоге мы получим следующий консольный вывод:
Tom (36) - Microsoft Alice (28) - Microsoft
Теперь для получения данных используем методы расширения LINQ:
using(ApplicationContext db = new ApplicationContext()) { var users = db.Users.Include(p=>p.Company).Where(p=> p.CompanyId == 1); }
Оба запроса в итоге транслируются в одно и то же выражение sql:
SELECT [p].[Id], [p].[CompanyId], [p].[Name], [p].[Age], [c].[Id], [c].[Name] FROM [Users] AS [p] INNER JOIN [Companies] AS [c] ON [p].[CompanyId] = [c].[Id] WHERE [p].[CompanyId] = 1
Основные методы, которые мы можем использовать для создания запросов в Entity Framework Core:
All / AllAsync: возвращает true, если все элементы набора удовлятворяют определенному условию, иначе возвращает false
Any / AnyAsync: возвращает true, если хотя бы один элемент набора определенному условию
Average / AverageAsync: подсчитывает cреднее значение числовых значений в наборе
Contains / ContainsAsync: определяет, содержит ли набор определенный элемент
Count / CountAsync: подсчитывает количество элементов в наборе
First / FirstAsync: выбирает первый элемент коллекции
FirstOrDefault / FirstOrDefaultAsync: выбирает первый элемент коллекции или возвращает значение по умолчанию
Single / SingleAsync: выбирает единственный элемент коллекции, если коллекция содердит больше или меньше одного элемента, то генерируется исключение
SingleOrDefault / SingleOrDefaultAsync: выбирает первый элемент коллекции или возвращает значение по умолчанию
Select: определяет проекцию выбранных значений
Where: определяет фильтр выборки
OrderBy: упорядочивает элементы по возрастанию
OrderByDescending: упорядочивает элементы по убыванию
ThenBy: задает дополнительные критерии для упорядочивания элементов возрастанию
ThenByDescending: задает дополнительные критерии для упорядочивания элементов по убыванию
Join: соединяет два набора по определенному признаку
GroupBy: группирует элементы по ключу
Except: возвращает разность двух наборов, то есть те элементы, которые содератся только в одном наборе
Union: объединяет два однородных набора
Intersect: возвращает пересечение двух наборов, то есть те элементы, которые встречаются в обоих наборах элементов
Sum / SumAsync: подсчитывает сумму числовых значений в коллекции
Min / MinAsync: находит минимальное значение
Max / MaxAsync: находит максимальное значение
Take: выбирает определенное количество элементов с начала последовательности
TakeLast: выбирает определенное количество элементов с конца последовательности
Skip: пропускает определенное количество элементов с начала последовательности
SkipLast: пропускает определенное количество элементов с конца последовательности
TakeWhile: возвращает цепочку элементов последовательности, до тех пор, пока условие истинно
SkipWhile: пропускает элементы в последовательности, пока они удовлетворяют заданному условию, и затем возвращает оставшиеся элементы
ToList / ToListAsync: получения списка объектов
Для большинства методов определены асинхронные версии, при необходимости получать данные в асинхронном режиме, мы можем их задействовать:
using System; using System.Linq; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; namespace HelloApp { public class Program { public static async Task Main(string[] args) { using (ApplicationContext db = new ApplicationContext()) { var users = await db.Users .Include(p => p.Company) .Where(p => p.CompanyId == 1) .ToListAsync(); // асинхронное получение данных foreach (var user in users) Console.WriteLine($"{user.Name} ({user.Age}) - {user.Company.Name}"); } Console.Read(); } } }