Skip to main content

How to Use ConfigureConventions in EF Core to Configure Every Property of a Type

· 7 min read
Jagdish Kumawat
Founder @ Dewiride

Some rules apply to every property of a type: all money is decimal(12,2), every DateTime is UTC, every enum is stored by name. Writing those rules property by property is repetitive, and the next property someone adds will miss them. EF Core's ConfigureConventions sets each rule once for the whole model. This guide writes three such rules, proves each one leaves the model unchanged when it replaces per-property code, and shows how a single property can still be different.

What ConfigureConventions Does​

EF Core's bulk configuration page explains: "EF Core allows the mapping configuration to be specified once for a given CLR type; that configuration is then applied to all properties of that type in the model as they are discovered." And where it goes: "Such configuration is applied by overriding ConfigureConventions on the type derived from DbContext."

Prerequisites​

Step 1: The Three Rules​

All three in the DbContext:

src/Dewiride.Inventory.Data/InventoryDbContext.cs (excerpt)
protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
ArgumentNullException.ThrowIfNull(configurationBuilder);

// Every amount of money: up to 10 digits before the decimal point and 2 after.
configurationBuilder.Properties<decimal>()
.HavePrecision(12, 2);

// Every date and time is stored as UTC and read back as UTC.
configurationBuilder.Properties<DateTime>()
.HaveConversion<UtcDateTimeConverter>();

// Every enum is stored as its name, such as 'Receipt', not as a number, so the
// tables still make sense to someone reading them without the C# code.
configurationBuilder.Properties<Enum>()
.HaveConversion<string>()
.HaveMaxLength(32);
}

The ConfigureConventions method in a macOS-style code window with three rules: every decimal gets precision 12 and scale 2, every DateTime uses the UtcDateTimeConverter, and every Enum is stored as a string of at most 32 characters.

  • Properties<decimal>().HavePrecision(12, 2) gives every decimal the same column type, decimal(12,2).
  • Properties<DateTime>().HaveConversion<UtcDateTimeConverter>() runs a value converter on every DateTime. How to Save and Read DateTime as UTC with EF Core and MySQL explains the converter.
  • Properties<Enum>()… matches every enum, because the type can be a base type: "The type specified in a call from ConfigureConventions can be a base type, an interface or a generic type definition." Every enum derives from System.Enum.

Step 2: Replace Per-Property Code, and Prove Nothing Changed​

Each rule here replaced configuration that used to be written on one property: HasPrecision(12, 2) on the product's price, HasConversion<string>().HasMaxLength(32) on the stock movement's type. After each replacement, EF Core can confirm the model is the same:

Terminal
dotnet ef migrations has-pending-model-changes --project src/Dewiride.Inventory.Data --startup-project src/Dewiride.Inventory.Api
Output (after moving the decimal rule, and again after the enum rule)
Build started...
Build succeeded.
No changes have been made to the model since the last migration.

A terminal where dotnet ef migrations has-pending-model-changes reports no changes after the decimal convention replaces the per-property precision, and again after the enum convention replaces the per-property conversion.

"No changes" means the conventions produce exactly the columns the per-property code did. Adding the DateTime converter didn't change the model either: a converter from DateTime to DateTime keeps the column type, so there's nothing for a migration to do.

Step 3: Make One Property Different​

Conventions set the default, not a law. A property can still be configured on its own, and that setting wins. To check, I gave the price a different precision on top of the convention:

src/Dewiride.Inventory.Data/Configurations/ProductConfiguration.cs (temporary)
builder.Property(p => p.UnitPrice)
.HasPrecision(10, 2);
Output (dotnet ef migrations has-pending-model-changes)
Changes have been made to the model since the last migration. Add a new migration.

The migration it produced changes only that one column:

Migrations/…_TryPrecisionOverride.cs (Up)
migrationBuilder.AlterColumn<decimal>(
name: "UnitPrice",
table: "products",
type: "decimal(10,2)",
precision: 10,
scale: 2,
nullable: false,
oldClrType: typeof(decimal),
oldType: "decimal(12,2)",
oldPrecision: 12,
oldScale: 2);

The per-property setting overrode the convention for UnitPrice, and every other decimal kept decimal(12,2). I removed that migration and the override afterwards, with dotnet ef migrations remove; this was only a test.

How the Layers Fit Together​

LayerWhereWins over
EF Core's built-in conventionsNothing to writeNothing
Data annotations, such as [MaxLength]On the entity's propertiesConventions
Pre-convention configurationConfigureConventionsConventions and data annotations
Fluent API for one propertyOnModelCreating or an IEntityTypeConfiguration classEverything above, as Step 3 showed

The third row comes from EF Core's docs: "Pre-convention configuration is equivalent to explicit configuration that is applied as soon as a matching object is added to the model. It will override all conventions and Data Annotations." So a [MaxLength(50)] attribute on a string doesn't beat a Properties<string>().HaveMaxLength(…) rule; a fluent HasMaxLength(50) does.

Common Mistakes​

MistakeWhat happensDo this instead
Putting type-wide rules in an entity's configuration classOther entities don't get themUse ConfigureConventions
Expecting an attribute to override a conventionThe convention winsUse the fluent API for exceptions
A convention that changes existing columns without a migrationThe model and database disagreeRun has-pending-model-changes, and add a migration when it reports changes

FAQ​

Do conventions apply to properties added later?​

Yes. They apply to every matching property "as they are discovered", including ones added long after the convention was written. That's their main benefit.

Can I write my own convention classes?​

Yes, EF Core supports model-building conventions as classes too. For rules like these three, ConfigureConventions is enough.

Conclusion​

ConfigureConventions sets a rule once for every property of a type: Properties<decimal>().HavePrecision(12, 2), Properties<DateTime>().HaveConversion<…>(), Properties<Enum>().HaveConversion<string>(). Replacing per-property code with a convention should leave the model unchanged, which dotnet ef migrations has-pending-model-changes confirms, and a fluent setting on a single property still overrides the convention when one property must be different.

Additional Resources​

Stay Updated

Subscribe to our newsletter for the latest tutorials, tech insights, and developer news.

By subscribing, you agree to our privacy policy. Unsubscribe at any time.