Fluent API in EF Core

What is Fluent API?

Fluent API provides a way to configure entity mappings and relationships using method chaining in the OnModelCreating method. It offers more configuration options than Data Annotations.

Basic Configuration

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Product>(entity =>
    {
        entity.HasKey(e => e.Id);
        entity.Property(e => e.Name).IsRequired().HasMaxLength(200);
        entity.Property(e => e.Price).HasColumnType("decimal(18,2)");
        entity.ToTable("Products");
    });
}

Property Configuration

modelBuilder.Entity<Product>()
    .Property(p => p.Name)
    .IsRequired()
    .HasMaxLength(200)
    .HasColumnName("ProductName")
    .HasDefaultValue("Unknown");

modelBuilder.Entity<Product>()
    .Property(p => p.Price)
    .HasColumnType("decimal(18,2)")
    .HasPrecision(18, 2);

modelBuilder.Entity<Product>()
    .Property(p => p.CreatedAt)
    .HasDefaultValueSql("GETUTCDATE()");

Primary Keys

// Single column primary key
modelBuilder.Entity<Product>()
    .HasKey(p => p.Id);

// Composite primary key
modelBuilder.Entity<OrderItem>()
    .HasKey(oi => new { oi.OrderId, oi.ProductId });

// Alternate key
modelBuilder.Entity<Product>()
    .HasAlternateKey(p => p.SKU);

Indexes

// Single column index
modelBuilder.Entity<Product>()
    .HasIndex(p => p.Name);

// Unique index
modelBuilder.Entity<User>()
    .HasIndex(u => u.Email)
    .IsUnique();

// Composite index
modelBuilder.Entity<Product>()
    .HasIndex(p => new { p.CategoryId, p.IsActive })
    .HasDatabaseName("IX_Product_Category_Active");

// Filtered index
modelBuilder.Entity<Product>()
    .HasIndex(p => p.Price)
    .HasFilter("[IsActive] = 1");

Relationships

// One-to-Many
modelBuilder.Entity<Post>()
    .HasOne(p => p.Blog)
    .WithMany(b => b.Posts)
    .HasForeignKey(p => p.BlogId)
    .OnDelete(DeleteBehavior.Cascade);

// One-to-One
modelBuilder.Entity<User>()
    .HasOne(u => u.Profile)
    .WithOne(p => p.User)
    .HasForeignKey<UserProfile>(p => p.UserId);

// Many-to-Many
modelBuilder.Entity<Student>()
    .HasMany(s => s.Courses)
    .WithMany(c => c.Students)
    .UsingEntity(j => j.ToTable("StudentCourses"));

Table Mapping

// Table name
modelBuilder.Entity<Product>()
    .ToTable("Products", schema: "dbo");

// Table splitting
modelBuilder.Entity<Order>()
    .ToTable("Orders");

modelBuilder.Entity<OrderDetails>()
    .ToTable("Orders");

modelBuilder.Entity<OrderDetails>()
    .HasOne(od => od.Order)
    .WithOne(o => o.Details)
    .HasForeignKey<OrderDetails>(od => od.OrderId);

Value Conversions

modelBuilder.Entity<Product>()
    .Property(p => p.Status)
    .HasConversion<string>();

modelBuilder.Entity<User>()
    .Property(u => u.DateOfBirth)
    .HasConversion(
        v => v.ToUniversalTime(),
        v => DateTime.SpecifyKind(v, DateTimeKind.Utc));

Global Query Filters

modelBuilder.Entity<Product>()
    .HasQueryFilter(p => !p.IsDeleted);

modelBuilder.Entity<Post>()
    .HasQueryFilter(p => p.IsPublished);

Owned Entities

modelBuilder.Entity<Order>().OwnsOne(o => o.ShippingAddress);
modelBuilder.Entity<Order>().OwnsOne(o => o.BillingAddress);

Summary

Fluent API in EF Core provides comprehensive configuration options for entities, properties, relationships, and database mappings through method chaining in OnModelCreating.

Test Your Knowledge

Take a quick quiz to test your understanding of this topic.

Test Your Efcore Knowledge

Ready to put your skills to the test? Take our interactive Efcore quiz and get instant feedback on your answers.