简体   繁体   English

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

[英]Re-creating naming conventions for EF6 in EF Core

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.我们正在将中等规模的应用程序从 Entity Framework 6.2 迁移到 Entity Framework Core 2.0,虽然很多事情比我想象的要容易,但我遇到了一个问题,我怀疑我是第一个(或第一个),但找不到紧凑的解决方案:命名约定。

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:在我们现有的数据库中,EF6 将其映射到以下表格:

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 .使用 EF Core 中的默认命名约定,移植的上下文似乎查找列FooId而不是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.) (我也有几个复杂类型,我用.Entity<Owner>().OwnsOne(o => o.Owned) ,所以解决方案也需要捕获它。)

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我已经针对这个问题在这里发布了一个 MVCE: 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. AFAIK 您不能更改 Shadow 属性的名称,但您可以更改表列的名称。 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.当然,如果您的模型中有外键属性,则不必这样做。

Here's how I managed to re-create the naming conventions for EF6 in EF Core 5.下面是我如何在 EF Core 5 中重新创建 EF6 的命名约定。

FK name from TableNameId to TableName_IdTableNameIdTableName_Id FK 名称

The most common scenario (AFAIK) is when your primary keys are simply set as Id in your models, as in:最常见的情况 (AFAIK) 是当您的主键在模型中简单地设置为Id时,如下所示:

public class Foo
{
    public int Id { get; set; }
    ...
}

The EF Core 5 default column name for foreign keys is TableNameId (eg FooId ). EF Core 5 外键的默认列名称是TableNameId (例如FooId )。 To create them in EF6 style TableName_Id (eg Foo_Id ), here is the configuration:要以Foo_Id样式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 name from TableNameId to TableName_TableNameIdTableNameIdTableName_TableNameId FK 名称

In the OP's case, the primary keys are set as TableNameId (eg FooId ) in the model.在 OP 的情况下,主键在模型中设置为TableNameId (例如FooId )。 In order to create foreign keys as TableName_TableNameId (eg Foo_FooId ), just replace these two methods from the previous example:为了将外键创建为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;
}

Notes:笔记:

  • I'm using .Single() in property.GetContainingForeignKeys().Single() since I didn't find any cases where that method didn't return only one entry.我在property.GetContainingForeignKeys().Single()使用.Single() ,因为我没有找到任何该方法不只返回一个条目的情况。
  • !name.Contains("_") is there to prevent overwriting any foreign key configurations you might have set manually. !name.Contains("_")用于防止覆盖您可能手动设置的任何外键配置。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM