We're in the process of migrating our moderately large application from Entity Framework 6.2 to Entity Framework Core 2.0, and while many things have been easier than I had dared to hope for, I've run into a problem that I doubt I'm the first (or the thousandth) one to have, but can't find a compact solution for it: Naming conventions.
Specifically, naming conventions for relational properties.
A typical one-to-many entity pair in our application looks something like this:
public class Foo
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int FooId { get; set; }
[Required]
public string Bar { get; set; }
[InverseProperty(nameof(Baz.Foo))]
public ICollection<Baz> Bazzes { get; set; }
}
public class Baz
{
[Key, DatabaseGenerated(DatabaseGeneratedOption.None)]
public int BazId { get; set; }
[Required]
public string Qux { get; set; }
[Required]
public Foo Foo { get; set; }
}
In our existing database, EF6 mapped that to the following tables:
Foos Bazzes -- named after the DbSets on the DbContext
**** ******
FooId : int BazId : int
Bar : nvarchar(max) Qux : nvarchar(max)
Foo_FooId : int
With the default naming conventions in EF Core, it seems that the ported context looks for a column FooId
instead of Foo_FooId
.
I've tried modifying this with something what's described here, but I can't figure out what condition to impose on the property metadata to only change the navigational properties. (I also have a couple of complex type, which I configure with .Entity<Owner>().OwnsOne(o => o.Owned)
, so the solution needs to capture that as well.)
Is there no ready-made solution to make this transition easier?
I've published an MVCE for this problem here: https://github.com/tlycken/MCVE_EfCoreNavigationalProperties
AFAIK you can't change the name of the Shadow Property, but you can change the name of the table column. Like this
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
//modelBuilder.Model.FindEntityType(typeof(Baz)).FindProperty("FooId").Relational().ColumnName = "Foo_FooId";
foreach( var m in modelBuilder.Model.GetEntityTypes())
{
foreach (var sp in m.GetProperties().Where( p => p.IsShadowProperty ))
{
var fk = sp.GetContainingForeignKeys().Where(f => f.Properties.Count == 1).FirstOrDefault();
if (fk != null && fk.PrincipalKey.Properties.Count == 1)
{
sp.Relational().ColumnName = fk.PrincipalEntityType.Relational().TableName + "_" + fk.PrincipalKey.Properties.Single().Relational().ColumnName;
}
}
}
}
Of course if you have a Foreign Key Property in your model, you wouldn't have to do this.