3. EF Core и база данных¶
Entity Framework Core позволяет работать с таблицами через C#-классы. В практике для простого запуска используется SQLite. В промышленном варианте можно заменить провайдер на PostgreSQL, MS SQL или MySQL.
Главная идея EF Core: разработчик описывает данные как классы C#, а EF Core берёт на себя преобразование этих классов в таблицы и SQL-запросы. Это не отменяет понимания базы данных, но снижает количество ручного SQL-кода.
В практике база нужна не просто “для галочки”. Она хранит историю событий. Если приложение перезапустить, события не должны пропасть. Поэтому данные сохраняются в SQLite-файл, а структура базы создаётся через миграции.
Установка пакетов¶
Эти пакеты подключают EF Core к проекту. Базовые библиотеки ставятся в Infrastructure, потому что именно там будет код работы с базой, а пакет Design добавляется в API, чтобы он мог быть startup-проектом для миграций.
dotnet add ActivityMonitoring.Infrastructure package Microsoft.EntityFrameworkCore
dotnet add ActivityMonitoring.Infrastructure package Microsoft.EntityFrameworkCore.Sqlite
dotnet add ActivityMonitoring.Infrastructure package Microsoft.EntityFrameworkCore.Design
dotnet add ActivityMonitoring.Api package Microsoft.EntityFrameworkCore.Design
Для PostgreSQL:
Для MS SQL:
Для MySQL:
Модели пользователей и ролей¶
Пользователь в системе нужен не только для входа. Через пользователя события получают автора: можно понять, кто чаще работает с системой, кто создаёт ошибки, какие роли активнее используют функциональность.
Роль нужна для разграничения доступа. Например, оператор может создавать события, аналитик может смотреть отчёты, администратор может удалять данные.
namespace ActivityMonitoring.Domain.Entities;
public class Role
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public List<AppUser> Users { get; set; } = new();
}
public class AppUser
{
public int Id { get; set; }
public string UserName { get; set; } = string.Empty;
public string PasswordHash { get; set; } = string.Empty;
public bool IsActive { get; set; } = true;
public int RoleId { get; set; }
public Role Role { get; set; } = null!;
public List<ActivityEvent> Events { get; set; } = new();
}
DbContext¶
AppDbContext — центральная точка EF Core. Через него приложение получает доступ к таблицам. Свойства DbSet<T> можно воспринимать как коллекции записей, которые EF Core умеет загружать из базы и сохранять обратно.
Метод OnModelCreating уточняет правила: уникальность логина, максимальная длина описания, начальные роли и связи между таблицами.
namespace ActivityMonitoring.Infrastructure.Persistence;
public class AppDbContext : DbContext
{
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options)
{
}
public DbSet<Role> Roles => Set<Role>();
public DbSet<AppUser> Users => Set<AppUser>();
public DbSet<ActivityEvent> ActivityEvents => Set<ActivityEvent>();
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Role>()
.HasIndex(x => x.Name)
.IsUnique();
modelBuilder.Entity<AppUser>()
.HasIndex(x => x.UserName)
.IsUnique();
modelBuilder.Entity<ActivityEvent>()
.Property(x => x.Description)
.HasMaxLength(500);
modelBuilder.Entity<ActivityEvent>()
.HasOne(x => x.User)
.WithMany(x => x.Events)
.HasForeignKey(x => x.UserId);
}
}
Подключение в Program.cs¶
После описания DbContext его нужно зарегистрировать в DI-контейнере. Эта строка говорит приложению, как создавать контекст базы данных и какую строку подключения использовать.
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlite(builder.Configuration.GetConnectionString("DefaultConnection")));
Сама строка подключения хранится в конфигурации API-проекта. Так код не привязывается к конкретному пути базы данных и может использовать разные настройки для разработки и запуска.
appsettings.json:
Миграции¶
Миграция — это история изменения базы данных. Сегодня вы добавили таблицу событий, завтра добавили поле Severity, послезавтра изменили индекс. EF Core сохраняет эти изменения как C#-файлы миграций, чтобы базу можно было воспроизвести на другом компьютере.
Команды ниже сначала устанавливают инструмент миграций, затем создают миграцию по текущим моделям и применяют её к SQLite-базе.
dotnet tool install --global dotnet-ef
dotnet ef migrations add InitialCreate --project ActivityMonitoring.Infrastructure --startup-project ActivityMonitoring.Api
dotnet ef database update --project ActivityMonitoring.Infrastructure --startup-project ActivityMonitoring.Api
Пример запроса EF Core¶
Этот запрос выбирает события за период, подгружает пользователя, сортирует новые события выше старых и ограничивает результат первыми 100 строками. Такой подход похож на SQL-запрос с JOIN, WHERE, ORDER BY и LIMIT, но записан на C# через LINQ.
var events = await db.ActivityEvents
.AsNoTracking()
.Include(x => x.User)
.Where(x => x.CreatedAt >= from && x.CreatedAt <= to)
.OrderByDescending(x => x.CreatedAt)
.Take(100)
.ToListAsync(ct);
AsNoTracking() используется для чтения, когда записи не планируется изменять. Это снижает накладные расходы EF Core.
Что проверить после настройки БД¶
- Проект собирается без ошибок.
- Команда
dotnet ef migrations add InitialCreateсоздаёт папкуMigrations. - Команда
dotnet ef database updateсоздаёт файл SQLite. - В базе есть таблицы
Roles,Users,ActivityEvents. - Начальные роли и пользователи добавлены через
HasData.