Запросы LINQ to Entities
Для взаимодействия с источником данных 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 Users { get; set; } = new List();
}
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 Companies { get; set; }
public DbSet 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();
}
}
}
Только полноправные пользователи могут оставлять комментарии. Аутентифицируйтесь пожалуйста, используя сервисы.