0Pricing
C# Academy · Урок

Операции CRUD с EF Core

Выполняйте операции создания, чтения, изменения и удаления с помощью отслеживания изменений EF Core и SaveChanges.

«Операции CRUD с EF Core» — бесплатный урок C# Academy на CoddyKit. Это урок 3 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения C# Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс C# Academy содержит 4 уроков всего.

Обзор CRUD в EF Core

EF Core поддерживает все четыре операции CRUD — создание, чтение, обновление и удаление — благодаря сочетанию отслеживания изменений и SaveChangesAsync(). Понимание механизма отслеживания изменений — ключ к освоению CRUD.

Создание: добавление новой сущности

Используйте DbSet.Add() или AddAsync(), чтобы пометить сущность как добавленную. При вызове SaveChangesAsync() EF Core сгенерирует INSERT и заполнит ключ с автоинкрементом.

var order = new Order
{
    CustomerId = 5,
    PlacedAt   = DateTime.UtcNow,
    Status     = OrderStatus.Pending
};

await _db.Orders.AddAsync(order);
await _db.SaveChangesAsync();

Console.WriteLine(order.Id); // populated by DB

Добавление графа сущностей

Добавление родительской сущности вместе с дочерними одним вызовом вставляет весь граф объектов. EF Core автоматически разрешает связи по FK.

var order = new Order
{
    CustomerId = 5,
    PlacedAt   = DateTime.UtcNow,
    Lines      = new List<OrderLine>
    {
        new() { ProductId = 10, Quantity = 2, Price = 49.99m },
        new() { ProductId = 11, Quantity = 1, Price = 19.99m }
    }
};

_db.Orders.Add(order);
await _db.SaveChangesAsync();
// Inserts Order + 2 OrderLines in one transaction

Чтение: запросы с LINQ

EF Core преобразует выражения LINQ в SQL. Составляйте запросы с помощью Where, Select, OrderBy и других операторов, а затем материализуйте результат с помощью ToListAsync или FirstOrDefaultAsync.

// Read all pending orders for a customer
var orders = await _db.Orders
    .Where(o => o.CustomerId == 5 && o.Status == OrderStatus.Pending)
    .OrderByDescending(o => o.PlacedAt)
    .Select(o => new OrderSummaryDto
    {
        Id     = o.Id,
        Total  = o.Lines.Sum(l => l.Quantity * l.Price),
        Status = o.Status.ToString()
    })
    .ToListAsync();

Чтение: поиск одной сущности

Для чтения одной сущности используйте FindAsync — сначала проверяется механизм отслеживания — или FirstOrDefaultAsync / SingleOrDefaultAsync с предикатом.

// By PK (checks cache first)
var order = await _db.Orders.FindAsync(orderId);

// By predicate (always hits DB)
var latest = await _db.Orders
    .Where(o => o.CustomerId == 5)
    .OrderByDescending(o => o.PlacedAt)
    .FirstOrDefaultAsync();

Обновление: отслеживание изменений

Сущности, загруженные через EF Core, отслеживаются. Изменяйте свойства напрямую и вызывайте SaveChangesAsync(). EF Core обнаружит различия и сгенерирует минимальный оператор UPDATE.

var order = await _db.Orders.FindAsync(orderId);
if (order is null) return;

order.Status   = OrderStatus.Shipped;
order.ShippedAt = DateTime.UtcNow;

await _db.SaveChangesAsync();
// UPDATE Orders SET Status=..., ShippedAt=... WHERE Id=orderId

Обновление: массовое обновление (EF Core 7+)

ExecuteUpdateAsync обновляет строки непосредственно в базе данных, не загружая сущности. Для массовых операций это значительно эффективнее.

// Mark all overdue orders as Cancelled — no loading needed
await _db.Orders
    .Where(o => o.Status == OrderStatus.Pending
             && o.PlacedAt < DateTime.UtcNow.AddDays(-30))
    .ExecuteUpdateAsync(set => set
        .SetProperty(o => o.Status, OrderStatus.Cancelled)
        .SetProperty(o => o.UpdatedAt, DateTime.UtcNow));

Удаление: удаление одной сущности

Вызовите Remove() для отслеживаемой сущности, а затем SaveChangesAsync(). Если у вас есть только ID, сначала найдите сущность или используйте ExecuteDeleteAsync.

var order = await _db.Orders.FindAsync(orderId);
if (order is null) return;

_db.Orders.Remove(order);
await _db.SaveChangesAsync();
// DELETE FROM Orders WHERE Id=orderId

Удаление: массовое удаление (EF Core 7+)

ExecuteDeleteAsync выполняет один SQL-оператор DELETE без загрузки сущностей — это идеально подходит для пакетной очистки данных.

// Delete all cancelled orders older than 1 year
int deleted = await _db.Orders
    .Where(o => o.Status == OrderStatus.Cancelled
             && o.PlacedAt < DateTime.UtcNow.AddYears(-1))
    .ExecuteDeleteAsync();

Console.WriteLine($"Deleted {deleted} old orders");

Транзакции

Все операции в рамках одного вызова SaveChangesAsync выполняются внутри транзакции. Для многоэтапных операций с несколькими сохранениями используйте явную транзакцию.

using var transaction = await _db.Database.BeginTransactionAsync();
try
{
    _db.Orders.Add(newOrder);
    await _db.SaveChangesAsync();

    inventory.Stock -= newOrder.Quantity;
    await _db.SaveChangesAsync();

    await transaction.CommitAsync();
}
catch
{
    await transaction.RollbackAsync();
    throw;
}

Практика: полноценный сервис CRUD

Полноценный сервис оборачивает все операции CRUD в надлежащую обработку ошибок и возвращает объекты передачи данных, чтобы не раскрывать внутреннее устройство сущностей.

public async Task<OrderDto> CreateOrderAsync(CreateOrderRequest req)
{
    var order = new Order { CustomerId = req.CustomerId, PlacedAt = DateTime.UtcNow };
    _db.Orders.Add(order);
    await _db.SaveChangesAsync();
    return new OrderDto { Id = order.Id, Status = order.Status.ToString() };
}

public async Task<bool> CancelOrderAsync(int id)
{
    int rows = await _db.Orders
        .Where(o => o.Id == id && o.Status == OrderStatus.Pending)
        .ExecuteUpdateAsync(s =>
            s.SetProperty(o => o.Status, OrderStatus.Cancelled));
    return rows > 0;
}

Быстрая проверка

В чём преимущество ExecuteUpdateAsync / ExecuteDeleteAsync по сравнению с загрузкой сущностей и вызовом SaveChangesAsync?

Итоги: операции CRUD с EF Core

Главное:

  • Add + SaveChangesAsync → INSERT; EF Core заполняет автоматически сгенерированные ключи
  • Изменение отслеживаемых сущностей + SaveChangesAsync → минимальный UPDATE
  • Remove + SaveChangesAsync → DELETE для отдельных сущностей
  • ExecuteUpdateAsync / ExecuteDeleteAsync → массовые операции без загрузки сущностей (EF Core 7+)
  • Оборачивайте многоэтапные операции в явную транзакцию, чтобы обеспечить атомарность

Часто задаваемые вопросы

Урок «Операции CRUD с EF Core» бесплатный?

Да — полный текст урока «Операции CRUD с EF Core» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс C# Academy, подпишись на CoddyKit PRO. Курс C# Academy содержит 4 уроков всего.

Чему я научусь в уроке «Операции CRUD с EF Core»?

Выполняйте операции создания, чтения, изменения и удаления с помощью отслеживания изменений EF Core и SaveChanges. Ты практикуешь C# Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать C# Academy?

Предыдущий опыт не требуется. C# Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 3 из 4.

Сколько времени занимает урок «Операции CRUD с EF Core»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке C# Academy?

Да. Каждый урок C# Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Основы DbContext и DbSet
  2. Миграции и управление схемой
  3. Операции CRUD с EF Core
  4. Связи: один-ко-многим и многие-ко-многим
← Назад к C# Academy