Entity FrameworkJuniorCoding
Что такое DbSet<T> и как его использовать?
DbSet<T> — это типизированный шлюз к таблице базы данных внутри DbContext. Через него выполняются LINQ-запросы, добавление, обновление и удаление сущностей типа T.
Что такое DbSet<T>
DbSet<T> — свойство DbContext, представляющее коллекцию всех сущностей типа T в базе данных. Он реализует IQueryable<T>, поэтому LINQ-выражения транслируются в SQL и выполняются на стороне базы данных, а не в памяти.
Типичные операции:
- Чтение:
FindAsync,FirstOrDefaultAsync,ToListAsync,Where,Include - Добавление:
Add/AddAsync/AddRange - Удаление:
Remove/RemoveRange - Изменение выполняется через свойства отслеживаемой сущности — EF Core фиксирует разницу сам.
Объявление в DbContext
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
// EF Core 6+ — рекомендуемый синтаксис
public DbSet<Product> Products => Set<Product>();
public DbSet<Order> Orders => Set<Order>();
}
Основные операции
// 1. Получить по первичному ключу (сначала смотрит в кэш, потом в БД)
var product = await db.Products.FindAsync(productId);
// 2. Фильтрация — SQL WHERE на стороне БД
var cheap = await db.Products
.Where(p => p.Price < 1000 && p.IsAvailable)
.OrderBy(p => p.Price)
.ToListAsync();
// 3. Добавление новой записи
var newProduct = new Product { Name = "Widget", Price = 499 };
db.Products.Add(newProduct);
await db.SaveChangesAsync(); // INSERT выполняется здесь
// 4. Обновление
var existing = await db.Products.FindAsync(productId);
if (existing is not null)
{
existing.Price = 599; // Change Tracker зафиксирует изменение
await db.SaveChangesAsync(); // UPDATE
}
// 5. Удаление
var toDelete = await db.Products.FindAsync(productId);
if (toDelete is not null)
{
db.Products.Remove(toDelete);
await db.SaveChangesAsync(); // DELETE
}
AsNoTracking для readonly-запросов
// Если данные нужны только для отображения — отключите отслеживание.
// Это быстрее и потребляет меньше памяти.
var catalog = await db.Products
.AsNoTracking()
.Where(p => p.CategoryId == categoryId)
.ToListAsync();
Подводные камни
- LINQ не всегда транслируется в SQL — вызов методов C# внутри
Where(например,.ToString(), пользовательские методы) вызоветInvalidOperationExceptionили загрузит все строки в память. Проверяйте сгенерированный SQL черезLogToилиToQueryString(). - Материализация происходит при ToList/FirstOrDefault — до этого момента запрос — это только выражение. Добавление
WhereпослеToListAsync()фильтрует уже загруженные данные в памяти. - N+1 запросов — обращение к навигационным свойствам в цикле без
Includeгенерирует отдельный SQL на каждую итерацию. - Add vs Attach —
Addпомечает сущность какAdded(будет INSERT). Если сущность уже есть в БД, используйтеAttach+ изменение свойств, иначе получите дубликат или ошибку уникальности. - Изменение detached-объекта — если объект получен вне текущего контекста (например, десериализован из JSON), Change Tracker о нём не знает. Нужно явно вызвать
db.Update(entity)илиdb.Entry(entity).State = EntityState.Modified. - Параллельные запросы к одному контексту —
DbSetне потокобезопасен. Нельзя запускать дваawait db.X.ToListAsync()черезTask.WhenAllна одном DbContext. - Отсутствие SaveChangesAsync —
Add/Removeтолько меняют состояние в трекере. БезSaveChangesAsync()изменения не попадут в базу данных. - Remove без загрузки — чтобы удалить без предварительного чтения, используйте
db.Products.Where(p => p.Id == id).ExecuteDeleteAsync()(EF Core 7+), а не загрузку + Remove.
Common mistakes
- Путать DbSet
как типизированный вход в запросы и изменения с похожим механизмом из другой версии или платформы. - Игнорировать runtime-границы Entity Framework: lifecycle, DI scope, SQL translation, UI thread или platform API.
- Не обсуждать null/empty/error cases и поведение под нагрузкой.
What the interviewer is testing
- Кандидат объясняет DbSet
как типизированный вход в запросы и изменения на конкретном примере, а не только определением. - Указывает последствия для производительности, тестируемости и поддержки.
- Различает документированное поведение текущего стека и устаревшие практики.