Entity Framework Core для C# (.NET): полное руководство с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Что такое Entity Framework Core и зачем он нужен

Entity Framework Core (EF Core) — это современная объектно-реляционная карта (ORM) для платформы .NET, разработанная компанией Microsoft. Она позволяет разработчикам работать с базами данных, используя объекты .NET, и избавляет от необходимости писать большую часть кода доступа к данным.

EF Core — это легковесная, расширяемая и кроссплатформенная версия Entity Framework. Она поддерживает множество СУБД, включая SQL Server, SQLite, PostgreSQL, MySQL, Oracle и другие.

Основные возможности EF Core:

  • Автоматическое создание и обновление схемы базы данных на основе модели (Code First)
  • Выполнение запросов к базе данных с помощью LINQ (Language Integrated Query)
  • Автоматическое отслеживание изменений объектов и генерация SQL-команд для обновления данных
  • Поддержка миграций для управления изменениями схемы базы данных
  • Поддержка отношений между сущностями (один-к-одному, один-ко-многим, многие-ко-многим)
  • Ленивая, жадная и явная загрузка связанных данных

Установка Entity Framework Core

Установка EF Core осуществляется через NuGet Package Manager. В отличие от Python, здесь используется dotnet add package или менеджер пакетов Visual Studio.

Установка через .NET CLI:

dotnet add package Microsoft.EntityFrameworkCore
dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Tools

Установка для PostgreSQL (Npgsql):

dotnet add package Npgsql.EntityFrameworkCore.PostgreSQL

Установка для SQLite:

dotnet add package Microsoft.EntityFrameworkCore.Sqlite

Установка для MySQL:

dotnet add package Pomelo.EntityFrameworkCore.MySql

Быстрый старт с примером кода

Создадим простое консольное приложение для работы с базой данных книг.

1. Создаем модель данных:

public class Book
{
    public int Id { get; set; }
    public string Title { get; set; }
    public string Author { get; set; }
    public int Year { get; set; }
    public decimal Price { get; set; }
}

public class Author
{
    public int Id { get; set; }
    public string Name { get; set; }
    public List<Book> Books { get; set; } = new();
}

2. Создаем контекст базы данных:

public class LibraryContext : DbContext
{
    public DbSet<Book> Books { get; set; }
    public DbSet<Author> Authors { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        optionsBuilder.UseSqlite("Data Source=library.db");
    }
}

3. Используем EF Core для работы с данными:

using var context = new LibraryContext();
context.Database.EnsureCreated(); // Создаем БД, если её нет

// Добавление данных
var author = new Author { Name = "Лев Толстой" };
author.Books.Add(new Book { Title = "Война и мир", Year = 1869, Price = 1500m });
author.Books.Add(new Book { Title = "Анна Каренина", Year = 1877, Price = 1200m });

context.Authors.Add(author);
context.SaveChanges();

// Чтение данных
var books = context.Books
    .Where(b => b.Year > 1850)
    .OrderBy(b => b.Year)
    .ToList();

foreach (var book in books)
{
    Console.WriteLine($"{book.Title} ({book.Year})");
}

Основные методы и функции Entity Framework Core

1. Add

Описание: Добавляет новую сущность в контекст в состоянии Added. При вызове SaveChanges() будет выполнена INSERT-команда.

Параметры:

  • object entity — экземпляр сущности для добавления

Пример:

var book = new Book { Title = "Преступление и наказание", Author = "Ф. Достоевский", Year = 1866, Price = 800m };
context.Books.Add(book);
context.SaveChanges();

2. AddRange

Описание: Добавляет коллекцию сущностей в контекст за один вызов. Эффективнее, чем множественные вызовы Add.

Параметры:

  • IEnumerable entities — коллекция сущностей для добавления

Пример:

var books = new List<Book>
{
    new Book { Title = "Идиот", Year = 1869, Price = 900m },
    new Book { Title = "Братья Карамазовы", Year = 1880, Price = 1100m }
};
context.Books.AddRange(books);
context.SaveChanges();

3. Find

Описание: Находит сущность по первичному ключу. Сначала проверяет, не загружена ли уже сущность в контекст (избегает лишнего запроса к БД).

Параметры:

  • params object[] keyValues — значения первичного ключа

Пример:

var book = context.Books.Find(1);
if (book != null)
{
    Console.WriteLine($"Найдена книга: {book.Title}");
}

4. FirstOrDefault / First

Описание: Возвращает первый элемент последовательности, удовлетворяющий условию. FirstOrDefault возвращает null, если элемент не найден, First выбрасывает исключение.

Параметры:

  • Expression<Func<T, bool>> predicate — условие фильтрации (опционально)

Пример:

var book = context.Books.FirstOrDefault(b => b.Title == "Война и мир");
if (book != null)
{
    Console.WriteLine($"Цена: {book.Price}");
}

// Без условия — первый элемент
var firstBook = context.Books.First();

5. Where

Описание: Фильтрует последовательность сущностей по заданному условию. Возвращает IQueryable, который можно дальше уточнять.

Параметры:

  • Expression<Func<T, bool>> predicate — условие фильтрации

Пример:

var expensiveBooks = context.Books
    .Where(b => b.Price > 1000m)
    .OrderByDescending(b => b.Price)
    .ToList();

foreach (var book in expensiveBooks)
{
    Console.WriteLine($"{book.Title} — {book.Price} руб.");
}

6. OrderBy / OrderByDescending

Описание: Сортирует результаты запроса по возрастанию (OrderBy) или убыванию (OrderByDescending).

Параметры:

  • Expression<Func<T, TKey>> keySelector — выражение для выбора ключа сортировки

Пример:

var sortedBooks = context.Books
    .OrderBy(b => b.Year)
    .ThenBy(b => b.Title)
    .ToList();

var sortedDesc = context.Books
    .OrderByDescending(b => b.Price)
    .ToList();

7. Include

Описание: Выполняет жадную загрузку (eager loading) связанных данных. Позволяет загрузить связанные сущности вместе с основной.

Параметры:

  • Expression<Func<T, TProperty>> navigationPropertyPath — навигационное свойство для загрузки

Пример:

var authorsWithBooks = context.Authors
    .Include(a => a.Books)
    .ToList();

foreach (var author in authorsWithBooks)
{
    Console.WriteLine($"Автор: {author.Name}");
    foreach (var book in author.Books)
    {
        Console.WriteLine($"  - {book.Title}");
    }
}

8. ThenInclude

Описание: Позволяет загружать связанные данные второго уровня (например, книги автора и их жанры).

Параметры:

  • Expression<Func<TPreviousProperty, TProperty>> navigationPropertyPath — навигационное свойство для загрузки

Пример:

var authorsWithBooksAndGenres = context.Authors
    .Include(a => a.Books)
        .ThenInclude(b => b.Genre) // Предполагаем, что у Book есть свойство Genre
    .ToList();

9. Update

Описание: Помечает сущность как измененную. При вызове SaveChanges() будет выполнена UPDATE-команда для всех свойств сущности.

Параметры:

  • object entity — экземпляр сущности для обновления

Пример:

var book = context.Books.Find(1);
if (book != null)
{
    book.Price = 2000m;
    context.Books.Update(book);
    context.SaveChanges();
}

10. Remove

Описание: Помечает сущность для удаления. При вызове SaveChanges() будет выполнена DELETE-команда.

Параметры:

  • object entity — экземпляр сущности для удаления

Пример:

var book = context.Books.Find(1);
if (book != null)
{
    context.Books.Remove(book);
    context.SaveChanges();
}

11. Any

Описание: Проверяет, существует ли хотя бы один элемент, удовлетворяющий условию. Эффективнее, чем Count() > 0, так как не загружает все данные.

Параметры:

  • Expression<Func<T, bool>> predicate — условие проверки (опционально)

Пример:

bool hasExpensiveBooks = context.Books.Any(b => b.Price > 5000m);
Console.WriteLine(hasExpensiveBooks ? "Есть дорогие книги" : "Нет дорогих книг");

12. Count

Описание: Возвращает количество элементов, удовлетворяющих условию. Выполняется на стороне сервера БД.

Параметры:

  • Expression<Func<T, bool>> predicate — условие подсчета (опционально)

Пример:

int bookCount = context.Books.Count();
int oldBooksCount = context.Books.Count(b => b.Year < 1900);
Console.WriteLine($"Всего книг: {bookCount}, старых книг: {oldBooksCount}");

Реальный практический пример использования

Создадим приложение для управления библиотекой с полным CRUD (Create, Read, Update, Delete).

using Microsoft.EntityFrameworkCore;

// Модели
public class Reader
{
    public int Id { get; set; }
    public string Name { get; set; }
    public string Email { get; set; }
    public List<Loan> Loans { get; set; } = new();
}

public class Loan
{
    public int Id { get; set; }
    public int BookId { get; set; }
    public Book Book { get; set; }
    public int ReaderId { get; set; }
    public Reader Reader { get; set; }
    public DateTime LoanDate { get; set; }
    public DateTime? ReturnDate { get; set; }
}

// Контекст
public class LibraryContext : DbContext
{
    public DbSet<Book> Books { get; set; }
    public DbSet<Author>                    

Рекомендаций