Introduction
When Data Annotations aren't enough, the Fluent API in OnModelCreating or IEntityTypeConfiguration<T> gives ShopNest Banking precise control over columns, indexes, concurrency, and value objects.
After this article you will
- Separate configuration with IEntityTypeConfiguration
- Map columns, schemas, indexes, defaults
- Configure owned entities and shadow properties
- Implement optimistic concurrency tokens
- Understand TPH inheritance mapping
Prerequisites
- Article 17 — EF Core Relationships
- ShopNest DbContext with Products/Orders from Articles 13–15
- SQL Server or LocalDB configured
Concept deep-dive
Fluent API vs Annotations: Both apply — Fluent wins on conflicts. Use Fluent for team-wide DB rules; Annotations for simple validation on POCOs.
public class AccountConfiguration : IEntityTypeConfiguration<Account>
{
public void Configure(EntityTypeBuilder<Account> b)
{
b.ToTable("Accounts", schema: "banking");
b.HasIndex(a => a.AccountNumber).IsUnique();
b.Property(a => a.Balance).HasPrecision(18, 2);
b.Property(a => a.RowVersion).IsRowVersion(); // concurrency
b.OwnsOne(a => a.Address, addr => {
addr.Property(x => x.City).HasMaxLength(80);
});
}
}
// DbContext
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfigurationsFromAssembly(typeof(ShopNestDbContext).Assembly);
}
Shadow properties: b.Property<DateTime>("LastModified"); — not on C# class but in DB.
TPH: Discriminator column for Account types (Savings/Current) in one table.
Hands-on — ShopNest Banking Application
- Account, Transaction entities with precision and unique AccountNumber index.
- AccountConfiguration class in Configurations/ folder.
- Test concurrency: two updates — DbUpdateConcurrencyException.
- Owned Address value object on Customer.
Common errors & best practices
- Precision missing on decimal — money rounding bugs in SQL.
- All config in one giant OnModelCreating — split into IEntityTypeConfiguration files.
Interview questions
Q: IEntityTypeConfiguration benefit?
A: One class per entity — clean, testable, discoverable via ApplyConfigurationsFromAssembly.
Q: Optimistic concurrency?
A: RowVersion/timestamp — SaveChanges fails if row changed since read; UI shows refresh message.
Summary
- Fluent API is the source of truth for complex mappings
- IEntityTypeConfiguration keeps DbContext thin
- Indexes and precision matter for banking-grade data
- Owned types model value objects without extra tables
Previous: EF Core Relationships
Next: EF Core Repository Pattern and Unit of Work
FAQ
Fluent vs Annotations — which wins?
Fluent API takes precedence when both configure the same property.
What is a shadow property?
Defined only in EF model, not on CLR class — useful for audit columns.