在EF Core中重新创建EF6的命名约定

时间:2017-12-06 16:40:10

标签: c# entity-framework entity-framework-6 entity-framework-core

我们正在将我们的中等大型应用程序从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

2 个答案:

答案 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 名称从 TableNameIdTableName_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 名称从 TableNameIdTableName_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("_") 用于防止覆盖您可能手动设置的任何外键配置。