我们正在将我们的中等大型应用程序从Entity Framework 6.2迁移到Entity Framework Core 2.0,虽然许多事情比我想要的更容易,但我遇到了一个问题。我怀疑我是第一个(或千分之一)的问题,但无法找到一个紧凑的解决方案:命名约定。
具体来说,是关系属性的命名约定。
我们的应用程序中典型的一对多实体对看起来像这样:
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; }
}
在我们现有的数据库中,EF6将其映射到以下表格:
Foos Bazzes -- named after the DbSets on the DbContext
**** ******
FooId : int BazId : int
Bar : nvarchar(max) Qux : nvarchar(max)
Foo_FooId : int
使用EF Core中的默认命名约定,移植的上下文似乎会查找列FooId
而不是Foo_FooId
。
我尝试使用here描述的内容对其进行修改,但我无法弄清楚要对属性元数据施加什么条件才能更改导航属性。 (我也有一些复杂的类型,我用.Entity<Owner>().OwnsOne(o => o.Owned)
配置,所以解决方案也需要捕获它。)
是否没有现成的解决方案来简化此过渡?
我已在此处发布了针对此问题的MVCE:https://github.com/tlycken/MCVE_EfCoreNavigationalProperties
答案 0 :(得分:1)
AFAIK您无法更改Shadow Property的名称,但您可以更改表列的名称。喜欢这个
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;
}
}
}
}
当然,如果您的模型中有外键属性,则不必执行此操作。
答案 1 :(得分:0)
这是我如何在 EF Core 5 中重新创建 EF6 的命名约定。
FK 名称从 TableNameId
到 TableName_Id
最常见的情况 (AFAIK) 是当您的主键在模型中简单地设置为 Id
时,如下所示:
public class Foo
{
public int Id { get; set; }
...
}
外键的 EF Core 5 默认列名称是 TableNameId
(例如 FooId
)。要以 EF6 样式 TableName_Id
(例如 Foo_Id
)创建它们,配置如下:
public class TestContext : DbContext
{
public DbSet<Foo> Foos { get; set; }
public DbSet<Baz> Bazzes { get; set; }
// ...
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
AddConfigurations(modelBuilder);
RenameForeignKeys(modelBuilder);
base.OnModelCreating(modelBuilder);
}
private static void AddConfigurations(ModelBuilder modelBuilder)
{
modelBuilder.ApplyConfiguration(new FooConfiguration());
}
private static void RenameForeignKeys(ModelBuilder modelBuilder)
{
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
var properties = entityType.GetProperties()
.Where(property => property.IsForeignKey() && !property.DeclaringEntityType.IsOwned());
foreach (var property in properties)
{
var newName = GetNewColumnName(property.Name);
property.SetColumnName(newName);
}
}
}
private static string GetNewColumnName(string name)
{
if (name.Length > 2 && name[^2..] == "Id" && name[^3..] != "_Id")
{
return $"{name[0..^2]}_Id";
}
return name;
}
}
public class FooConfiguration : IEntityTypeConfiguration<Foo>
{
public void Configure(EntityTypeBuilder<Foo> builder)
{
}
}
FK 名称从 TableNameId
到 TableName_TableNameId
在 OP 的情况下,主键在模型中设置为 TableNameId
(例如 FooId
)。要将外键创建为 TableName_TableNameId
(例如 Foo_FooId
),只需替换前面示例中的这两个方法:
private static void RenameForeignKeys(ModelBuilder modelBuilder)
{
foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
var properties = entityType.GetProperties()
.Where(property => property.IsForeignKey() && !property.DeclaringEntityType.IsOwned());
foreach (var property in properties)
{
var foreignKey = property.GetContainingForeignKeys().Single();
var principalEntityName = foreignKey.PrincipalEntityType.FullName();
var newName = GetNewColumnName(property.Name, principalEntityName);
property.SetColumnName(newName);
}
}
}
private static string GetNewColumnName(string name, string tableName)
{
if (name.Length > 2 && name[^2..] == "Id" && !name.Contains("_"))
{
return $"{name[0..^2]}_{tableName}Id";
}
return name;
}
注意事项:
.Single()
中使用了 property.GetContainingForeignKeys().Single()
,因为我没有发现该方法不只返回一个条目的任何情况。!name.Contains("_")
用于防止覆盖您可能手动设置的任何外键配置。