Запросы LINQ to Entities

Введение в LINQ to Entities

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

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

Для взаимодействия с источником данных 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();
        }
    }
}
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850