- 🧱 EF Core 8's
ConfigureComplexProperties()makes renaming complex property columns much easier. - 🔀 Column renaming now uses only the fluent API, not Data Annotations, for complex or owned types.
- 🧠 You can set up nested complex types, even if they are inside others, for clear database mapping.
- 🗃️ Renaming columns is important when your models need to match older databases or to keep names consistent.
- ⚠️ Using the wrong builder levels or forgetting
[Owned]can cause EF Core to fail without warning.
EF Core Complex Property Column Names: How to Rename Them in EF Core 8+
Entity Framework Core (EF Core) has ways to design data models and decide how data is saved in databases. An important area that people sometimes don't understand is how complex (owned) types become columns, and how to rename these columns correctly. This helps make names clear, follow usual rules, or work with older databases. EF Core 8 has a better way to rename these complex property columns using Fluent APIs like ConfigureComplexProperties(). This helps developers design their database tables better.
Understanding Complex Types in EF Core
Complex types—once called "owned types"—are a way in EF Core to keep related data together without making a new database table. This is useful in Domain-Driven Design (DDD) where value objects are common.
For instance, think about how to model a user's address:
public class User
{
public int Id { get; set; }
public Address Address { get; set; }
}
[Owned]
public class Address
{
public string Street { get; set; }
public string City { get; set; }
}
In these setups, the Address fields (Street, City) are put into columns in the Users table. By default, EF Core takes the name of the property (Address) and the field name to create column names like Address_Street and Address_City.
Advantages of Complex Types
- They cut down on repeated data: you can group fields that you use often.
- They help model your domain better: value objects look more natural.
- Less setup code: you don't need to declare more tables.
Limitations Without Custom Column Naming
- Automatic column names can get long and confusing.
- Names like
Billing_Address_CityorExperience_Location_Citymake the database layout messy. - It gets hard when using other tools or older databases that need different names.
Why Rename Complex Property Columns?
Renaming column mappings for complex properties is not just about how things look. It helps in real projects.
Key Reasons To Rename
- 🛠️ Matching older databases: Make names match existing database layouts.
- 🧮 Easier to read SQL: Shorter, clearer names make queries easier to understand.
- 🎯 Keep names consistent: Stop names from changing across different entities.
- 🔑 Stop name clashes: Custom setups make it less likely for column names to clash when you use many complex types.
For example, without renaming, you might end up with:
SELECT Address_Street, Address_City FROM Users
But with renaming, this becomes cleaner:
SELECT StreetName, CityName FROM Users
These changes can make things much better when you work with reporting tools, SQL queries, and other programs.
EF Core Changes: Before vs. EF Core 8+
Before EF Core 8, you set up column names inside owned types by using .OwnsOne() with .Property() and .HasColumnName():
modelBuilder.Entity<User>()
.OwnsOne(u => u.Address)
.Property(p => p.City)
.HasColumnName("CityName");
What's New in EF Core 8?
EF Core 8 has a new Fluent API. It uses .ComplexProperty() and .ConfigureComplexProperties():
modelBuilder.Entity<User>()
.ComplexProperty(u => u.Address)
.Property(p => p.City)
.HasColumnName("CityName");
Or to set up things in a more organized way:
modelBuilder.Entity<User>()
.ConfigureComplexProperties(cb =>
{
cb.Property(p => p.City).HasColumnName("CityName");
});
Benefits of This Update
- It makes setting up complex types easier.
- It lets you keep different parts of the setup separate by using organized builder functions.
- It makes nested classes safer and clearer to use.
- And then it makes it possible to set up types that are nested inside other types, like an address inside a location.
Introducing RelationalComplexTypePropertyBuilderExtensions
EF Core 8 also added new ways to extend code through the Microsoft.EntityFrameworkCore namespace. These work right on complex property setups.
How to Turn on Fluent Renaming for Complex Types
First, add the needed namespace:
using Microsoft.EntityFrameworkCore;
Then use:
modelBuilder.Entity<User>()
.ComplexProperty(u => u.Address)
.Property(p => p.Street)
.HasColumnName("StreetName");
These new extensions let you do everything you could only do with .OwnsOne() in older versions. But now it is cleaner and better organized.
Renaming Complex Property Columns in EF Core 8+
Let’s look at a full example where we rename a property inside a complex type.
Model Definition
public class Experience
{
public int Id { get; set; }
public Location Location { get; set; }
}
[Owned]
public class Location
{
public string City { get; set; }
public string Region { get; set; }
}
Usually, columns will be made like Location_City and Location_Region. To change City to City_Name:
modelBuilder.Entity<Experience>()
.ComplexProperty(e => e.Location)
.Property(l => l.City)
.HasColumnName("City_Name");
Or by using a builder that keeps things separate:
modelBuilder.Entity<Experience>()
.ConfigureComplexProperties(locationBuilder =>
{
locationBuilder.Property(l => l.City).HasColumnName("City_Name");
});
This is very helpful when you work on big projects because putting setup logic in one place makes it easier to keep up to date.
Fluent API vs Data Annotations: Why Fluent API Wins
Data annotations like [Column] might work for simple cases. But they do not work well with complex types.
Example That Doesn’t Work (In Complex Type):
[Column("City_Name")] // Does not work automatically in [Owned] types
public string City { get; set; }
Fluent API is the only sure way to rename columns inside complex or nested types.
Use Fluent API When
- You need exact control over how entities map to columns.
- Your project has complex types inside other complex types.
- You need to change names or database layouts for different setups.
- You are adding other features like indexes or calculated properties.
Renaming Nested Complex Property Columns
So what happens when your complex types themselves hold other complex types?
Example
public class Order
{
public ShippingInfo Shipping { get; set; }
}
[Owned]
public class ShippingInfo
{
public Address Address { get; set; }
}
[Owned]
public class Address
{
public string Street { get; set; }
}
Fluent Setup for Nesting
modelBuilder.Entity<Order>()
.ConfigureComplexProperties(shipping =>
{
shipping.ConfigureComplexProperties(address =>
{
address.Property(a => a.Street)
.HasColumnName("Shipping_Street");
});
});
This way of doing things works through many levels. It gives you full control over how each property maps to your database layout.
Good Ways to Do Nested Mappings
- Keep property names short and clear.
- Don't chain more than 2–3 levels deep unless you have a good reason.
- Separate setup logic using
IEntityTypeConfiguration<>.
Compound Keys, Shadow Properties, and Complex Types
EF Core works with shadow properties. These are columns that EF tracks, but you don't define them in your class. You can use them inside complex types.
Setting Up a Shadow Column With a New Name:
complexBuilder.Property<string>("GeoHash")
.HasColumnName("LocationHash");
Rules for Shadow Properties
- Use them for calculated columns, extra data, or foreign keys.
- Always make sure names and their casing are consistent.
- Don't use deeply nested shadows unless you really need them for performance or how things are set up.
Checking Renamed Columns with Migrations
You must check your changes using EF Core Migrations. This makes sure renamed columns are applied the right way.
Command to Make a Migration
dotnet ef migrations add RenameColumnExample
What to Look For
In your generated migration file (RenameColumnExample.cs):
b.Property<string>("City_Name")
.HasColumnType("nvarchar(max)");
If the name you expect does not show up, double-check:
- How you used
.ConfigureComplexProperties() - If the
[Owned]attribute is there - And that the property path matches your model's structure
Making EF Core Work with Older Databases by Renaming Columns
Renaming complex property columns is very helpful when you connect EF Core to older databases that use different names.
Advice for Older Database Layouts
- Use
HasColumnName()to match existing column names. - You can reverse engineer your database (
Scaffold-DbContext), then use Fluent API to rename things. This helps you change your model safely. - Use EF Core to map complex models, but keep the old names.
This way, you can use EF Core and its speed without having to change your current database setup.
Fixing Problems and Common Mistakes
People often run into these problems when renaming columns for complex properties:
- ❌ Duplicate Setups: Setting up the same property in both
OnModelCreatingandIEntityTypeConfiguration<>. - ❌ Missing
[Owned]: If you leave out this attribute, EF will see the type as a navigation entity instead. - ❌ Wrong Builder Level: Calling
.Property()on the wrong part of the builder (like on the main entity instead of the complex property). - ❌ Wrong Package: Using EF Core 6/7 code in an EF Core 8+ project, or if
Microsoft.EntityFrameworkCore.Relationalis missing.
To stop hidden failures or wrong database setup, make sure things are built correctly. Also, check migration results every time.
Good Ways to Make Models That Are Clean and Easy to Update
- 📁 Separate Setups: Use
IEntityTypeConfiguration<>to keep mapping logic in parts and use it again. - 🧱 Name Consistently: Use a naming rule like
Street_Name,City_Nameto make sure everything looks the same. - 🧼 Keep Fluent Setups Fluent: Do not break builder chains if you don't need to. Use the fluent style so it is easy to read.
- 🔍 Check Output Carefully: Always make sure migration results match what you expect.
Sample Setup File (Good Structure)
public class UserConfiguration : IEntityTypeConfiguration<User>
{
public void Configure(EntityTypeBuilder<User> builder)
{
builder.ComplexProperty(u => u.Address)
.Property(a => a.Street)
.HasColumnName("Addr_Street");
}
}
This helps teams work together, test, and bring new people on board. It keeps the model and database logic connected and easy to see.
In Summary
EF Core 8 brought useful updates for working with complex types, especially in how database columns are named and set up. When developers use ConfigureComplexProperties() and other Fluent APIs, they can get database layouts that are exact, easy to read, and simple to update. This is true no matter how complex the project or if there are older database needs.
Start using EF Core 8's new Fluent API features. They help with a modern, rule-based, code-first way of working that is good for big projects. Make test projects, prepare your migrations with care, and add these methods. This will keep your backend clean and ready for the future.
Citations
Microsoft. (2023). Entity Framework Core 8.0 Preview Documentation. Retrieved from https://learn.microsoft.com/en-us/ef/core/what-is-new/ef-core-8.0/
Microsoft. (2022). Handling Owned Entity Types. Retrieved from https://learn.microsoft.com/en-us/ef/core/modeling/owned-entities