EF Core 10 Renamed Columns You Didn't Touch

You upgrade to EF Core 10, run dotnet ef migrations add out of habit, and it generates a migration that renames columns on a model you didn't change at all. This isn't a bug in your project — it's two related breaking changes to how EF Core names complex type columns, both aimed at closing the same class of silent bug.

reference guide EF Core .NET 10

// change 1 — colliding column names no longer silently share a column

A complex type's properties become columns on the owning entity's table. If two different complex-type properties end up resolved to the same column name — commonly because both were given the same explicit HasColumnName — earlier EF Core versions let them share that one physical column without complaint. Writing to one property could silently overwrite what the other one had stored, because as far as the database was concerned, they were never two things.

what actually happens, step by step
the model
public class Customer
{
    public int Id { get; set; }
    public Address ShippingAddress { get; set; }
    public Address BillingAddress { get; set; }
}

public class Address
{
    public string Street { get; set; }
}

modelBuilder.Entity<Customer>(b =>
{
    b.ComplexProperty(c => c.ShippingAddress, p => p.Property(a => a.Street).HasColumnName("Street"));
    b.ComplexProperty(c => c.BillingAddress,  p => p.Property(a => a.Street).HasColumnName("Street"));
});
on EF Core 9 and earlier
Both ComplexProperty calls map to the same "Street" column. No error at migration time, no error at runtime -- customer.ShippingAddress.Street and customer.BillingAddress.Street silently read and write the exact same value.
on EF Core 10, after upgrading
dotnet ef migrations add PostUpgradeCheck generates a migration renaming one of the columns --
e.g. "Street" stays for ShippingAddress, BillingAddress's Street becomes "Street1" --
so the two properties finally get separate storage instead of quietly sharing one.
This is the change worth reading the migration diff for, not just applying it: EF Core just told you two properties were colliding on one column. If that table already has rows, the newly-separated column starts out with whatever the shared column already held — check whether that's actually the right data for both properties before trusting it.

// change 2 — nested complex types now use the full property path

A complex type can itself contain another complex type. Before EF Core 10, a nested complex type's columns were named from the nested type alone, dropping the outer property entirely — so Order.ShippingAddress.Coordinates.Lat mapped to a column named just Coordinates_Lat, identical to whatever Order.BillingAddress.Coordinates.Lat would also produce. Same silent-collision risk as change 1, just one level deeper in the object graph.

the model that triggers it
public class Order
{
    public int Id { get; set; }
    public Address ShippingAddress { get; set; }
    public Address BillingAddress { get; set; }
}

public class Address
{
    public string Street { get; set; }
    public GeoCoordinates Coordinates { get; set; }   // a complex type nested inside a complex type
}

public class GeoCoordinates
{
    public double Lat { get; set; }
    public double Lng { get; set; }
}
the column name itself, before vs. after
EF Core 9 and earlierCoordinates_Lat — just the nested type's name, so ShippingAddress.Coordinates.Lat and BillingAddress.Coordinates.Lat resolve to the identical column name
EF Core 10ShippingAddress_Coordinates_Lat and BillingAddress_Coordinates_Lat — the full path from the entity down, so the two stay distinct on their own
If you never nest one complex type inside another, this specific change doesn't affect you — only change 1 (the uniquification suffix) can still apply to top-level complex properties.

// keeping your existing column names

Both changes only kick in when EF Core is deriving a name on its own. Explicit configuration always wins, on any EF Core version — so if a rename would break existing views, reports, or raw SQL that reference the old column name, pin it down instead of letting the migration go through:

pin the column name explicitly, at any nesting depth
modelBuilder.Entity<Order>()
    .ComplexProperty(e => e.ShippingAddress)
    .ComplexProperty(o => o.Coordinates)
    .Property(c => c.Lat)
    .HasColumnName("Coordinates_Lat");   // keeps the pre-EF-Core-10 name on purpose

Either way, treat the migration EF Core generates right after the upgrade as a diagnostic, not a formality: it's pointing at every complex-type column name that changed, which is also exactly the set of names that were ambiguous before you upgraded.

// if this is part of a wider .NET 10 migration

This tends to surface in the same sitting as other EF Core migration cleanup — if Update-Database is also reporting nothing to apply when it should be, that's a separate, unrelated mismatch worth ruling out first. And if the database involved is SQLite, .NET 10 also changed how Microsoft.Data.Sqlite interprets stored timestamps — the same class of bug as this one: code that still runs, just resolves differently now.

// frequently asked questions

Why did dotnet ef migrations add rename columns I never touched after upgrading to EF Core 10?

EF Core 10 changed how it names columns for complex types in two ways: it now appends a numeric suffix when two complex-type properties would otherwise map to the same column name, and nested complex types now use the full property path instead of just the nested type's name. Neither requires you to have changed your model — the new migration is EF Core re-deriving names under the new rules.

Is the EF Core 10 complex type column naming change fixing a real bug?

Yes for the collision case. Before EF Core 10, if two different complex-type properties resolved to the same column name, EF Core let them silently share one physical column — writes to one property could overwrite the other. EF Core 10 detects the collision and appends a numeric suffix so each property gets its own column instead.

How do I keep my existing EF Core column names after upgrading to EF Core 10?

Configure the column name explicitly with HasColumnName on each affected property inside ComplexProperty(). Explicit configuration always wins over the automatic naming rules, so this works whether the rename came from the uniquification suffix or the new nested full-path naming.

What is a complex type in EF Core?

A complex type is a class mapped as part of its owning entity rather than as its own table — its properties become columns on the entity's table, prefixed by the complex property's name by default (for example, an Address complex type on a ShippingAddress property produces ShippingAddress_Street). They were introduced as a first-class concept in EF Core 8.