С помощью атрибутов и Fluent API для сущостей и их свойств можно установить многочисленные настройки. Однако, если настроек очень много, то они могут утяжелять класс контекста и сущностей. В этом случае Entity Framework Core позволяет вынести конфигурацию сущностей в отдельные классы.
Для вынесения конфигурации во вне необходимо создать класс конфигурации, реализующий интерфейс EntityTypeConfiguration<T>.
К примеру, пусть у нас есть следующий класс контекста и моделей:
using Microsoft.EntityFrameworkCore; public class ApplicationContext : DbContext { public DbSet<User> Users { get; set; } = null!; public DbSet<Company> Companies { get; set; } = null!; public ApplicationContext() { Database.EnsureDeleted(); Database.EnsureCreated(); } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlite("Data Source=helloapp.db"); } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<User>().ToTable("People").Property(p => p.Name).IsRequired(); modelBuilder.Entity<User>().Property(p => p.Id).HasColumnName("user_id"); modelBuilder.Entity<Company>().ToTable("Enterprises") .Property(c => c.Name).IsRequired(); } } public class User { public int Id { get; set; } public string? Name { get; set; } public int Age { get; set; } } public class Company { public int Id { get; set; } public string? Name { get; set; } }
Вся конфигурация здесь определена в методе OnModelCreating(). В принципе он не содержит много кода, однако при наличии гораздо большего количества сущностей и более изощренной их конфигурации с помощью Fluent API данный метод мог бы сильно раздуться в размерах. И теперь изменим определение контекста, применив конфигурации:
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; public class ApplicationContext : DbContext { public DbSet<User> Users { get; set; } = null!; public DbSet<Company> Companies { get; set; } = null!; public ApplicationContext() { Database.EnsureDeleted(); Database.EnsureCreated(); } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlite("Data Source=helloapp.db"); } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.ApplyConfiguration(new UserConfiguration()); modelBuilder.ApplyConfiguration(new CompanyConfiguration()); } } public class UserConfiguration : IEntityTypeConfiguration<User> { public void Configure(EntityTypeBuilder<User> builder) { builder.ToTable("People").Property(p => p.Name).IsRequired(); builder.Property(p => p.Id).HasColumnName("user_id"); } } public class CompanyConfiguration : IEntityTypeConfiguration<Company> { public void Configure(EntityTypeBuilder<Company> builder) { builder.ToTable("Enterprises").Property(c => c.Name).IsRequired(); } } public class User { public int Id { get; set; } public string? Name { get; set; } public int Age { get; set; } } public class Company { public int Id { get; set; } public string? Name { get; set; } }
Теперь конфигурация моделей вынесена в отдельные классы. А для добавления конкретных конфигураций в контекст используется метод modelBuilder.ApplyConfiguration(), которому передается нужный объект конфигурации. В итоге по своему действию первый и второй варианты контекста будут идентичны.
В качестве альтернативы мы могли бы использовать еще один вариант. Вместо выделения отдельных классов конфигураций определить конфигурацию в виде отдельных методов в том же классе контекста данных:
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; public class ApplicationContext : DbContext { public DbSet<User> Users { get; set; } = null!; public DbSet<Company> Companies { get; set; } = null!; public ApplicationContext() { Database.EnsureDeleted(); Database.EnsureCreated(); } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlite("Data Source=helloapp.db"); } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<User>(UserConfigure); modelBuilder.Entity<Company>(CompanyConfigure); } // конфигурация для типа User public void UserConfigure(EntityTypeBuilder<User> builder) { builder.ToTable("People").Property(p => p.Name).IsRequired(); builder.Property(p => p.Id).HasColumnName("user_id"); } // конфигурация для типа Company public void CompanyConfigure(EntityTypeBuilder<Company> builder) { builder.ToTable("Enterprises").Property(c => c.Name).IsRequired(); } }
Здесь конфигурация определяется для каждого типа в отдельном методе, который в качестве параметра принимает объект EntityTypeBuilder<T>
.
Затем метод передается в вызов modelBuilder.Entity<T>()
для соответствующей модели.
Еще один альтернативный вариант применения конфигураций представляет атрибут EntityTypeConfiguration, который применяется к сущности и который получает тип класса конфигурации:
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; public class ApplicationContext : DbContext { public DbSet<User> Users { get; set; } = null!; public DbSet<Company> Companies { get; set; } = null!; public ApplicationContext() { Database.EnsureDeleted(); Database.EnsureCreated(); } protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseSqlite("Data Source=helloapp.db"); } } public class UserConfiguration : IEntityTypeConfiguration<User> { public void Configure(EntityTypeBuilder<User> builder) { builder.ToTable("People").Property(p => p.Name).IsRequired(); builder.Property(p => p.Id).HasColumnName("user_id"); } } public class CompanyConfiguration : IEntityTypeConfiguration<Company> { public void Configure(EntityTypeBuilder<Company> builder) { builder.ToTable("Enterprises").Property(c => c.Name).IsRequired(); } } [EntityTypeConfiguration(typeof(UserConfiguration))] public class User { public int Id { get; set; } public string? Name { get; set; } public int Age { get; set; } } [EntityTypeConfiguration(typeof(CompanyConfiguration))] public class Company { public int Id { get; set; } public string? Name { get; set; } }