Single Responsibility Principle in Entity Framework Configurations

Josiah T Mahachi
3 min readOct 10, 2023

--

Cleaning Up Your Entity Configurations with IEntityTypeConfiguration<T> in Entity Framework

Entity Framework (EF) is my go-to Object-Relational Mapping (ORM) tool for .NET development, especially when using a SQL Server Database. As the EF ecosystem has matured, various ways to configure entities have emerged. One such method is the IEntityTypeConfiguration<T> interface. In today's blog post, we'll take a sneak peek into why IEntityTypeConfiguration<T> is your codebase's tidying-up fairy and how you can employ it for a clean, maintainable context configuration.

But first,…:

“Why did the database admin leave his job? Because he didn’t like the relationship with tables!” 😂

Alright, let’s dive in.

The Problem with Inline Entity Configuration

To paint a picture, imagine a library system DbContext with all entity configurations set up inline. As your application grows and more entities are added, this DbContext can become unwieldy. For instance:

public class ApplicationContext : DbContext
{
public DbSet<Book> Books { get; set; }
public DbSet<Author> Authors { get; set; }
// ... more DbSets

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Book configurations
modelBuilder.Entity<Book>().HasKey(b => b.Id);
modelBuilder.Entity<Book>().Property(b => b.Title).IsRequired().HasMaxLength(200);
// ... other Book configurations

// Author configurations
modelBuilder.Entity<Author>().HasKey(a => a.Id);
modelBuilder.Entity<Author>().Property(a => a.Name).IsRequired().HasMaxLength(100);
// ... other Author configurations

// ... and many more entity configurations
}
}

Imagine having 20 entities or more. The OnModelCreating method becomes a giant maze of configurations, making it hard to read, maintain, and modify. If our DbContext were a book, it'd be the "War and Peace" of code—overwhelmingly long and intimidating.

Introducing IEntityTypeConfiguration<T>

IEntityTypeConfiguration<T> provides a way to separate out the configuration of each entity into its class. Each of these classes will focus solely on configuring a single entity, thereby promoting the Single Responsibility Principle (SRP).

Let’s refactor the above example using IEntityTypeConfiguration<T>:

1. Create Configuration Classes

public class BookConfiguration : IEntityTypeConfiguration<Book>
{
public void Configure(EntityTypeBuilder<Book> builder)
{
builder.HasKey(b => b.Id);
builder.Property(b => b.Title).IsRequired().HasMaxLength(200);
// ... other Book configurations
}
}
public class AuthorConfiguration : IEntityTypeConfiguration<Author>
{
public void Configure(EntityTypeBuilder<Author> builder)
{
builder.HasKey(a => a.Id);
builder.Property(a => a.Name).IsRequired().HasMaxLength(100);
// ... other Author configurations
}
}

2. Refactor the DbContext

public class ApplicationContext : DbContext
{
public DbSet<Book> Books { get; set; }
public DbSet<Author> Authors { get; set; }
// ... more DbSets

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new BookConfiguration());
modelBuilder.ApplyConfiguration(new AuthorConfiguration());
// ... apply other configurations
}
}

…or better still…

public class ApplicationContext : DbContext
{
public DbSet<Book> Books { get; set; }
public DbSet<Author> Authors { get; set; }
// ... more DbSets

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Applies all configurations from the assembly of the specified type (e.g., BookConfiguration)
modelBuilder.ApplyConfigurationsFromAssembly(typeof(BookConfiguration).Assembly);
}
}

When using ApplyConfigurationsFromAssembly, you're telling EF Core to scan the assembly where your specified type (in this case, BookConfiguration) resides. EF Core will then look for all classes that implement IEntityTypeConfiguration<T> and apply their configurations automatically.

Benefits:

  • Simplicity: It abstracts away the reflection code, making the DbContext cleaner and easier to read.
  • Automatic Detection: When adding a new entity configuration, there’s no need to remember to register it manually; it’s detected and applied automatically, provided it’s in the specified assembly.

Considerations:

  • Assembly Scanning: Make sure the type you provide (e.g., BookConfiguration) is in the same assembly as all your other configurations. Otherwise, they won't be detected.
  • If you split configurations across multiple assemblies, you'll need to call ApplyConfigurationsFromAssembly multiple times, once for each assembly.

Benefits of Using IEntityTypeConfiguration<T>

  1. Separation of Concerns: Each entity’s configuration is now in its class and can even be in its own file. It’s more readable and adheres to the SRP.
  2. Easier Maintenance: Need to modify the configuration of an entity? Just open its configuration class/file.
  3. Reusable Configurations: If you have multiple contexts, you can reuse the same configurations without duplication.

Wrapping Up

Just like how Marie Kondo helps people declutter their homes, IEntityTypeConfiguration<T> helps declutter your Entity Framework configurations. With each entity configuration in its rightful place, your DbContext becomes a much more joyful space.

And remember:

“Why did the developer keep his DbContext tidy? So he wouldn’t query chaos!” 😜

Keep coding and keep your configurations tidy!

--

--

Josiah T Mahachi

Full-stack Developer, C#, ASP.Net, Laravel, React Native, PHP, Azure Dev Ops, MSSQL, MySQL