Конфигурация сущностей в EF Core

от автора

Почти каждый (ну или почти каждый) разработчик слышал про такую технологию программирования, как ORM (англ. Oblect-Relational Mapping) или, если по-русски — объектно-реляционное отображение. Данная технология связывает базы данных с концепциями объектно-ориентированного программирования и, благодаря ей, многие разработчики уже стали забывать о работе с базами данных в чистом виде, например, создавая структуру данных непосредственно в СУБД.
Платформа .NET имеет кросплатформенную реализацию технологии ORM — Entity Framework Core (далее EF Core). С помощью данной технологии мы в принципе можем абстрагироваться от особенностей работы конкретной СУБД и в большинстве случаев, не подвергая модификации код нашего продукта перейти на любую другую СУБД.
Существует несколько вариантов конфигурирования сущностей, о которых мы и будем говорить в данной статье.

Дальнейшее рассмотрение способов конфигурирования подразумевает, что Вы уже знакомы с EF Core и можете поднять самый простой проект работающий с СУБД, использующий данную технологию.
Итак, давайте рассмотрим способы, как можно сконфигурировать сущности и отношения между ними.

Способ первый — атрибуты аннотаций данных.

Данный способ является самым простым для понимания, но недостаточно гибким и читабельным на мой субъективный взгляд. Суть его заключается в следующем — в классе сущности, представляющей модель предметной области с помощью атрибутов аннотаций данных из пространства System.ComponentModel.DataAnnotations настраивается маппинг к объекту СУБД:

[Table("SomeTable")] public class SomeEntity {   [Key]   public Guid Id { get; set; }   [Column("SomeName")]   [MaxLength(20)]   public string Name { get;set; } }

Выглядит не очень красиво, согласитесь?

Способ второй — FluentAPI.

Данный способ понравится поклонникам паттерна проектирования Fluent interface (текучий интерфейс), который позволяет множественно вызывать методы объекта, тем самым повышая читабельность кода. Сконфигурировать сущность с помощью FluentAPI можно несколькими способами.

Способ второй с половиной — FluentAPI в переопределенном методе OnModelCreating класса, наследующего поведение DbContext (предположим, что у нас есть модель, описанная в первом способе):

public class SomeDbContext: DbContext  {           protected override void OnModelCreating(ModelBuilder modelBuilder)     {         modelBuilder.Entity<SomeEntity>().ToTable("SomeTable")         .HasKey(k => k.Id);         modelBuilder.Entity<SomeEntity>()         .Property(p => p.Name)                 .HasColumnName("SomeName")                 .HasMaxLength(20);     } }

Таким образом, мы сконфигурировали одну сущность. А теперь представьте, какая «портянка» кода будет, если у нас хотя бы 10 таблиц в базе данных, в среднем имеющих 5 полей? Мда уж, такой поворот событий нас вряд ли устроит. Давайте перейдем к более лаконичному способу.

Способ второй, без половин — FluentAPI с помощью реализации интерфейса IEntityTypeConfiguration пространства Microsoft.EntityFrameworkCore. Данный способ позволяет «разложить по полочкам» конфигурации и отделить их от простейших POCO-классов, представляющих наши сущности. Для реализации данного способа необходимо создать класс, реализующий вышеуказанный интерфейс и его единственный метод Configure:

public class SomeEntityConfiguration : IEntityTypeConfiguration<SomeEntity> { public void Configure(EntityTypeBuilder<SomeEntity> builder) {     builder.ToTable("SomeTable");          .HasKey(k => k.Id); builder.Property(p => p.Name)          .HasColumnName("SomeName")                .HasMaxLength(20); } }

Затем в методе OnModelCreating нашего контекста мы применим нашу конфигурацию:

public class SomeDbContext: DbContext  {           protected override void OnModelCreating(ModelBuilder modelBuilder)     {         modelBuilder.ApplyConfigurationsFromAssembly(Assembly.GetExecutingAssembly());     } }

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

Способы конечно между собой различаются, и по-своему хороши для различных ситуаций.
Когда например у нас намечается несколько объектов, без каких либо сложных связей между собой — атрибуты аннотаций помогут нам быстро поднять решение для работы.
Если у нас несколько объектов и имеются между ними сложные связи, а также поля объектов имеют много специфичных свойств — FluentAPI в методе OnModelCreating поможет быстро и наглядно описать сущности и связи между ними.
Ну и наконец, если объектов много, конфигурировать сущности необходимо со связями, описывать специфичные свойства полей — реализация интерфейса IEntityTypeConfiguration — самое то! А если у объектов есть общие поля (например, Время создания, кто создал и т.п.) — можем определить базовый класс для сущностей, базовую конфигурацию. Тогда конфигурации остальных сущностей будут наследовать уже от базовой нашей конфигурации, переопределять метод Configure, вызывать в нем базовую реализацию и после определять специфичную для текущей сущности конфигурацию.

P.S. Буду рад, если в комментариях увижу ещё способы, не рассмотренные в данной статье.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.

Каким из способов пользуетесь Вы?

30.61% Атрибуты аннотации данных30
22.45% Fluent API в OnModelCreating22
44.9% Реализация IEntityTypeConfiguration44
2.04% Свой вариант (напишу в комментариях)2

Проголосовали 98 пользователей. Воздержались 22 пользователя.

ссылка на оригинал статьи https://habr.com/ru/articles/649647/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *