EF Core - Fluent API Configuration

The Fluent API in Entity Framework Core (EF Core) provides a powerful and flexible way to configure your data models. It allows you to define model configurations in code, offering a more expressive and customizable approach than data annotations. This tutorial covers the basics of using the Fluent API to configure your data models effectively.


1. Setting Up Fluent API Configuration

The Fluent API is accessed through the OnModelCreating method of your DbContext class. Use this method to configure your entities using the Fluent API.

        
            
public class ApplicationDbContext : DbContext
{
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Product>(entity =>
        {
            entity.HasKey(e => e.Id);
            entity.Property(e => e.Name).IsRequired().HasMaxLength(100);
        });
    }
}
        
    

In this example, we define configurations for the Product entity using the ModelBuilder.


2. Using Separate Configuration Classes

To keep your configurations organized and maintainable, you can use separate configuration classes for each entity. Implement the IEntityTypeConfiguration<TEntity> interface for your configuration classes.

        
            
public class ProductConfiguration : IEntityTypeConfiguration<Product>
{
    public void Configure(EntityTypeBuilder<Product> builder)
    {
        builder.HasKey(e => e.Id);
        builder.Property(e => e.Name).IsRequired().HasMaxLength(100);
        builder.Property(e => e.Price).HasColumnType("decimal(18,2)");
    }
}
        
    

This approach promotes separation of concerns and makes your codebase easier to manage.


3. Applying Single Configuration Class

Use the ApplyConfiguration method in the OnModelCreating method to apply your separate configuration classes to the model.

        
            
public class ApplicationDbContext : DbContext
{
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.ApplyConfiguration(new ProductConfiguration());
    }
}
        
    

This example shows how to apply a configuration class for the Product entity.


4. Applying Global Configuration Classes

Apply all configuration classes globally by using reflection to load all classes that implement the IEntityTypeConfiguration<TEntity> interface.

        
            
public class ApplicationDbContext : DbContext
{
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        // Apply all configurations in the assembly
        modelBuilder.ApplyConfigurationsFromAssembly(typeof(ApplicationDbContext).Assembly);
    }
}
        
    

This method automatically applies all configurations found in the assembly.


5. Configuring Properties

Use the Fluent API to configure various aspects of entity properties, such as data types, lengths, and constraints.

        
            
modelBuilder.Entity<Product>(entity =>
{
    entity.Property(e => e.Name)
          .IsRequired()
          .HasMaxLength(100);

    entity.Property(e => e.Price)
          .HasColumnType("decimal(18,2)");
});
        
    

This example demonstrates how to set the maximum length of a string property and configure a property to use a specific column type.


6. Configuring Relationships

The Fluent API allows you to define relationships between entities, including one-to-one, one-to-many, and many-to-many relationships.

        
            
modelBuilder.Entity<Order>(entity =>
{
    entity.HasMany(e => e.OrderItems)
          .WithOne(e => e.Order)
          .HasForeignKey(e => e.OrderId);
});
        
    

In this example, a one-to-many relationship is configured between the Order and OrderItem entities.


7. Configuring Keys and Indexes

Use the Fluent API to define primary keys, composite keys, and indexes on entity properties.

        
            
modelBuilder.Entity<Product>(entity =>
{
    entity.HasKey(e => new { e.Id, e.SKU });

    entity.HasIndex(e => e.Name)
          .HasDatabaseName("IX_Product_Name");
});
        
    

This example shows how to define a composite key and create an index on a property.


8. Configuring Table and Column Mapping

You can customize the table and column mappings for your entities using the Fluent API, specifying table names, schema, and column names.

        
            
modelBuilder.Entity<Product>(entity =>
{
    entity.ToTable("tbl_Products", "sales");

    entity.Property(e => e.Name)
          .HasColumnName("ProductName");
});
        
    

This example demonstrates how to map an entity to a specific table and configure column names.


9. Configuring Value Conversions

Use value conversions to store custom data types in your database by converting them to compatible database types.

        
            
modelBuilder.Entity<Product>(entity =>
{
    entity.Property(e => e.Status)
          .HasConversion<string>();
});
        
    

This example illustrates how to use value conversions for storing enumeration values as strings.


10. Configuring Global Query Filters

Global query filters allow you to define common filtering logic applied to all queries for a particular entity type.

        
            
modelBuilder.Entity<Product>(entity =>
{
    entity.HasQueryFilter(e => e.IsDeleted == false);
});
        
    

This example demonstrates how to use a global query filter to implement soft delete functionality.


11. Configuring Owned Entity Types

Owned entity types are used to model complex types that do not have their own identity and are owned by a parent entity.

        
            
modelBuilder.Entity<Customer>(entity =>
{
    entity.OwnsOne(e => e.Address);
});
        
    

This example shows how to define an owned entity type for the Address property of a Customer entity.


12. Configuring Complex Types

Complex types in EF Core represent a group of properties that are treated as a single unit, useful for storing aggregated information.

        
            
modelBuilder.Entity<Product>(entity =>
{
    entity.OwnsOne(e => e.Dimensions, dimensions =>
    {
        dimensions.Property(d => d.Length).HasColumnName("Length");
        dimensions.Property(d => d.Width).HasColumnName("Width");
        dimensions.Property(d => d.Height).HasColumnName("Height");
    });
});
        
    

This example illustrates how to configure a complex type for storing dimensions of a product.


13. Integrating FluentValidation with DI

FluentValidation is a popular library for building strongly-typed validation rules. Integrate it with ASP.NET Core's Dependency Injection (DI) to validate your models.

        
            
public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        // Add FluentValidation services
        services.AddControllers();
        services.AddValidatorsFromAssemblyContaining<ProductValidator>();
        services.AddFluentValidationAutoValidation();
    }
}
        
    

This example shows how to configure a single FluentValidation validator in your ASP.NET Core application.


14. Integrating FluentValidation with DI (Global)

Register all FluentValidation validators globally using Dependency Injection, allowing your application to automatically discover and use them.

        
            
public class Startup
{
    public void ConfigureServices(IServiceCollection services)
    {
        // Add FluentValidation services globally
        services.AddControllers();
        services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly());
        services.AddFluentValidationAutoValidation();
    }
}
        
    

This method ensures that all validators in the executing assembly are registered and available for use.



15. Best Practices for Fluent API Configuration

Here are some best practices for using the Fluent API in EF Core:


Summary

The EF Core Fluent API provides a robust way to configure your data models, offering flexibility and precision in defining entity relationships, constraints, and mappings. By following best practices and leveraging the Fluent API, you can create efficient and scalable data models for your applications.