Операции 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 — локальная установка не требуется.
Все уроки этого курса
- Основы DbContext и DbSet
- Миграции и управление схемой
- Операции CRUD с EF Core
- Связи: один-ко-многим и многие-ко-многим