Как стать автором
Обновить

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

Время на прочтение3 мин
Количество просмотров9K

Почти каждый (ну или почти каждый) разработчик слышал про такую технологию программирования, как 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. Буду рад, если в комментариях увижу ещё способы, не рассмотренные в данной статье.

Только зарегистрированные пользователи могут участвовать в опросе. Войдите, пожалуйста.
Каким из способов пользуетесь Вы?
31.25% Атрибуты аннотации данных30
22.92% Fluent API в OnModelCreating22
43.75% Реализация IEntityTypeConfiguration42
2.08% Свой вариант (напишу в комментариях)2
Проголосовали 96 пользователей. Воздержался 21 пользователь.
Теги:
Хабы:
-1
Комментарии26

Публикации

Истории

Работа

Ближайшие события

Weekend Offer в AliExpress
Дата20 – 21 апреля
Время10:00 – 20:00
Место
Онлайн
Конференция «Я.Железо»
Дата18 мая
Время14:00 – 23:59
Место
МоскваОнлайн