Entity Framework Fluent API

The Entity Framework (EF) Fluent API provides a way to configure your entity models using code, offering more granular control over mapping and behavior than data annotations alone. It's a powerful tool for customizing how EF interacts with your database.

Why Use the Fluent API?

Configuring Your Model with the Fluent API

The primary way to use the Fluent API is by overriding the OnModelCreating method in your derived DbContext class. Within this method, you'll use the ModelBuilder object to define your configurations.

Example: Basic Configuration

Let's say you have an entity Product and you want to configure its mapping to a table named Inventory and specify the data type and length of a property.


    public class ApplicationDbContext : DbContext
    {
        public DbSet<Product> Products { get; set; }

        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : base(options) { }

        protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            // Configure the Product entity
            modelBuilder.Entity<Product>().ToTable("Inventory"); // Maps Product to the Inventory table

            modelBuilder.Entity<Product>().Property(p => p.Name)
                .HasColumnName("ProductName") // Renames the column to ProductName
                .HasColumnType("NVARCHAR(100)") // Sets the SQL Server data type and length
                .IsRequired(); // Makes the column non-nullable

            modelBuilder.Entity<Product>().Property(p => p.Price)
                .HasPrecision(18, 2); // Sets precision and scale for decimal types

            modelBuilder.Entity<Product>().HasKey(p => p.ProductId); // Explicitly sets the primary key
        }
    }

    public class Product
    {
        public int ProductId { get; set; }
        public string Name { get; set; }
        public decimal Price { get; set; }
    }
            

Common Configuration Scenarios

Table Mapping

Use ToTable() to specify the database table name for an entity.


    modelBuilder.Entity<Customer>().ToTable("Clients");
            

Primary Key Configuration

Use HasKey() to specify the primary key for an entity. If your primary key follows EF's naming conventions (e.g., Id or EntityNameId), EF will often discover it automatically.


    modelBuilder.Entity<Order>().HasKey(o => o.OrderId);
            

Property Configuration

Use Property() followed by chaining methods to configure column properties like name, type, length, nullability, and precision.


    modelBuilder.Entity<User>().Property(u => u.Email)
        .HasColumnName("UserEmail")
        .HasMaxLength(255)
        .IsRequired();
            

Relationships (One-to-Many, Many-to-One, One-to-One, Many-to-Many)

The Fluent API offers powerful ways to define and configure relationships.

One-to-Many Relationship

Configuring a one-to-many relationship between Category and Product (one category has many products).


    modelBuilder.Entity<Category>()
        .HasMany(c => c.Products) // A category has many products
        .WithOne(p => p.Category) // A product has one category
        .HasForeignKey(p => p.CategoryId); // The foreign key property in Product
            
Many-to-Many Relationship

EF can automatically create a join table for many-to-many relationships if you define them correctly.


    modelBuilder.Entity<Post>()
        .HasMany(p => p.Tags)
        .WithMany(t => t.Posts)
        .Map(m =>
        {
            m.ToTable("PostTags"); // Name of the join table
            m.MapLeftKey("PostId"); // Foreign key column for Post
            m.MapRightKey("TagId"); // Foreign key column for Tag
        });
            

Ignoring Entities or Properties

Sometimes you might have classes or properties that you don't want EF to map to the database.


    // Ignore an entire entity
    modelBuilder.Ignore<ReportGenerator>();

    // Ignore a specific property
    modelBuilder.Entity<Order>().Ignore(o => o.InternalNotes);
            

Value Converters

Use value converters to map CLR types to different database types or to perform custom data transformations.


    modelBuilder.Entity<Settings>().Property(s => s.JsonData)
        .HasConversion(
            v => JsonSerializer.Serialize(v, null), // CLR to DB
            v => JsonSerializer.Deserialize<JsonDocument>(v, null)); // DB to CLR
            
Tip: For complex configurations involving multiple entities and relationships, consider creating separate configuration classes that implement IEntityTypeConfiguration<TEntity> and apply them using modelBuilder.ApplyConfiguration(). This promotes better organization and reusability.

Entity Type Configuration Classes

A more organized approach for larger models is to create classes that encapsulate the configuration for a specific entity. These classes implement the IEntityTypeConfiguration<TEntity> interface.


    public class ProductConfiguration : IEntityTypeConfiguration<Product>
    {
        public void Configure(EntityTypeBuilder<Product> builder)
        {
            builder.ToTable("Inventory");
            builder.Property(p => p.Name)
                .HasColumnName("ProductName")
                .HasColumnType("NVARCHAR(100)")
                .IsRequired();
            builder.Property(p => p.Price)
                .HasPrecision(18, 2);
            builder.HasKey(p => p.ProductId);
        }
    }

    // In your DbContext:
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.ApplyConfiguration(new ProductConfiguration());
        // Apply other configurations...
    }
            

Built-in Conventions

EF has a set of built-in conventions that automatically configure your model based on common patterns. For example, it typically infers:

The Fluent API allows you to override these default conventions.

Conclusion

The Entity Framework Fluent API is an essential tool for developers working with EF. It provides the flexibility and control needed to map your domain models accurately to your database schema, handle complex relationships, and customize various aspects of EF's behavior. Mastering the Fluent API will enable you to build more robust and efficient data access layers for your .NET applications.