DEV Community

mohamed Tayel
mohamed Tayel

Posted on • Edited on

EFCore Tutorial P4:Cleaning Up `OnModelCreating`

As your Entity Framework Core model grows, managing the configuration logic in the OnModelCreating method can become challenging. To keep the code clean, maintainable, and scalable, it’s important to modularize the entity configurations. In this article, we’ll explore three approaches to clean up the configuration logic in OnModelCreating:

  1. Using IEntityTypeConfiguration for the Product entity
  2. Using Extension Methods for the Category entity
  3. Using Partial Classes for the ProductSupplier entity

1. Using IEntityTypeConfiguration for Product

IEntityTypeConfiguration is a great way to modularize the configuration logic for each entity into its own class. Let’s see how we can use this approach to configure the Product entity.

Step 1: Create the ProductConfiguration Class

Create a new class ProductConfiguration.cs to configure the Product entity separately:

using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;

public class ProductConfiguration : IEntityTypeConfiguration<Product>
{
    public void Configure(EntityTypeBuilder<Product> builder)
    {


        builder.Property(p => p.Price)
               .HasColumnType("decimal(18,2)");



        // One-to-One relationship with Inventory
        builder.HasOne(p => p.Inventory)
               .WithOne(i => i.Product)
               .HasForeignKey<Inventory>(i => i.ProductId);


    }
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Apply Configuration in AppDbContext

In AppDbContext, use ApplyConfiguration to apply the Product configuration:

public class AppDbContext : DbContext
{
    public DbSet<Product> Products { get; set; }
    public DbSet<Category> Categories { get; set; }
    public DbSet<Inventory> Inventories { get; set; }
    public DbSet<Supplier> Suppliers { get; set; }
    public DbSet<ProductSupplier> ProductSuppliers { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // Apply Product configuration using IEntityTypeConfiguration
        modelBuilder.ApplyConfiguration(new ProductConfiguration());
    }
}
Enter fullscreen mode Exit fullscreen mode

This approach separates the Product configuration logic, making the AppDbContext cleaner and easier to maintain.


2. Using Extension Methods for Category

Using extension methods is another great way to modularize configuration logic for an entity. Let’s apply this approach to the Category entity.

Step 1: Create Extension Method

Create an extension method to configure the Category entity in a new file called ModelBuilderExtensions.cs:

public static class ModelBuilderExtensions
{
    public static void ConfigureCategory(this ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Category>(entity =>
        {
            entity.HasKey(c => c.Id);
            entity.Property(c => c.Name).IsRequired().HasMaxLength(50);

            // One-to-Many relationship with Products
            entity.HasMany(c => c.Products)
                  .WithOne(p => p.Category)
                  .HasForeignKey(p => p.CategoryId);
        });
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Apply Extension Method in AppDbContext

In AppDbContext, use the extension method to apply the Category configuration:

public class AppDbContext : DbContext
{
    public DbSet<Product> Products { get; set; }
    public DbSet<Category> Categories { get; set; }
    public DbSet<Inventory> Inventories { get; set; }
    public DbSet<Supplier> Suppliers { get; set; }
    public DbSet<ProductSupplier> ProductSuppliers { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // Apply Product configuration using IEntityTypeConfiguration
        modelBuilder.ApplyConfiguration(new ProductConfiguration());

        // Apply Category configuration using extension method
        modelBuilder.ConfigureCategory();
    }
}
Enter fullscreen mode Exit fullscreen mode

Using extension methods for the Category configuration makes it easier to extend and maintain your code.


3. Using Partial Classes for ProductSupplier

When you have complex relationships like Many-to-Many configurations, using partial classes allows you to distribute the configuration logic across multiple files, keeping the code modular and clean. Let’s apply this approach to configure the ProductSupplier entity.

Step 1: Create Partial Class for ProductSupplier Configuration

Create a new partial class in a separate file named AppDbContext.ProductSupplierConfiguration.cs to configure the ProductSupplier entity:

public partial class AppDbContext : DbContext
{
    private void ConfigureProductSupplier(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<ProductSupplier>()
            .HasKey(ps => ps.Id); // Define primary key for ProductSupplier

        modelBuilder.Entity<ProductSupplier>()
            .HasOne(ps => ps.Product) // Configure the relationship to Product
            .WithMany(p => p.ProductSuppliers)
            .HasForeignKey(ps => ps.ProductId);

        modelBuilder.Entity<ProductSupplier>()
            .HasOne(ps => ps.Supplier) // Configure the relationship to Supplier
            .WithMany(s => s.ProductSuppliers)
            .HasForeignKey(ps => ps.SupplierId);

        modelBuilder.Entity<ProductSupplier>()
            .ToTable("ProductSuppliers"); // Define the table name
    }
}
Enter fullscreen mode Exit fullscreen mode

Step 2: Modify OnModelCreating to Call the Partial Class Method

Now, in the main AppDbContext class, call the ConfigureProductSupplier method in OnModelCreating:

public partial class AppDbContext : DbContext
{
    public DbSet<Product> Products { get; set; }
    public DbSet<Category> Categories { get; set; }
    public DbSet<Inventory> Inventories { get; set; }
    public DbSet<Supplier> Suppliers { get; set; }
    public DbSet<ProductSupplier> ProductSuppliers { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // Apply Product configuration using IEntityTypeConfiguration
        modelBuilder.ApplyConfiguration(new ProductConfiguration());

        // Apply Category configuration using extension method
        modelBuilder.ConfigureCategory();

        // Apply ProductSupplier configuration using partial class method
        ConfigureProductSupplier(modelBuilder);
    }
}
Enter fullscreen mode Exit fullscreen mode

By splitting the configuration logic for ProductSupplier into a partial class, the code is easier to manage, especially as the number of entities and relationships grows.


Conclusion

By applying these techniques, we achieve a more modular, maintainable, and scalable codebase in Entity Framework Core:

  1. Using IEntityTypeConfiguration for the Product entity ensures that each entity has its configuration class, which improves modularity.
  2. Using Extension Methods for the Category entity provides a flexible way to apply configurations that can be reused across different contexts.
  3. Using Partial Classes for the ProductSupplier entity allows you to split large and complex configurations into smaller, more manageable pieces, keeping the DbContext class clean.

These strategies will help you maintain a cleaner OnModelCreating method and improve the maintainability of your codebase.
Source Code EFCoreDemo

Top comments (0)