.net核心实体框架(EF core)表命名约定

本文关键字:core 命名约定 EF 核心 实体 框架 net | 更新日期: 2023-09-27 18:07:22

.net core实体框架(EF core)表命名约定复数为single/simple/underscore

作为single simple underscore表名命名惯例的粉丝,我对EF core命名表Plural PascalCase的方式感到不舒服。

public class SourceType {
   ... 

DbContext

public class ApplicationDbContext : DbContext {
    public DbSet<SourceType> SourceTypes { get; set; }
    ...

创建名为SourceTypes (PascalCase and复数)

的表

我知道我可以通过在模型类中使用[table('source_type')]来更改生成的表名。

但是,我需要的是一个方法来做它在全局的方式。

.net核心实体框架(EF core)表命名约定

我知道这个问题很老了,已经有答案了,但是这个NuGet (EFCore.NamingConventions)可能很有趣。

这是一个NuGet包,它处理像

一样简单的命名约定
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    => optionsBuilder
        .UseNpgsql(...)
        .UseSnakeCaseNamingConvention();

还支持:

  • UseSnakeCaseNamingConvention: FullName变为full_name
  • UseLowerCaseNamingConvention: FullName变为fullname
  • UseCamelCaseNamingConvention: FullName变为fullName
  • UseUpperCaseNamingConvention: FullName变为FULLNAME

总之

使用扩展方法扩展ModelBuilder,执行一些正则表达式,并在DbContext中调用该方法

编辑:你也可以使用这个第三方库EFCore.NamingConventions

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
      => optionsBuilder
        .UseNpgsql(...)
        .UseSnakeCaseNamingConvention();
详细

ModelBuilder类创建扩展

public static class ModelBuilderExtensions 
{
    public static void SetSimpleUnderscoreTableNameConvention(this ModelBuilder modelBuilder)
    {
        foreach (IMutableEntityType entity in modelBuilder.Model.GetEntityTypes())
        {                        
            Regex underscoreRegex = new Regex(@"((?<=.)[A-Z][a-zA-Z]*)|((?<=[a-zA-Z])'d+)");            
            entity.Relational().TableName = underscoreRegex.Replace(entity.DisplayName(), @"_$1$2").ToLower();
        }
    }
}

在你的DbContext

中调用这个方法
public class ApplicationDbContext : DbContext
{
    public DbSet<SourceType> SourceTypes { get; set; }
    ...
   
    protected override void OnModelCreating(ModelBuilder builder)
    {
        base.OnModelCreating(builder);
        ...
        builder.SetSimpleUnderscoreTableNameConvention();
    }
}

我希望这将帮助任何像我一样的开发人员不要浪费时间寻找解决方案。:)

Faraj的答案不适用于ThreeCapitalWords,结果是three_capitalwords

下面是我的解决方案,基于这个答案:

    /// <summary>
    ///
    /// </summary>
    /// <param name="preserveAcronyms">If true, 'PrepareXXRecord' converted to 'prepare_xx_record',
    /// otherwise to 'prepare_xxrecord'</param>
    public static void SetSimpleUnderscoreTableNameConvention(this ModelBuilder modelBuilder, bool preserveAcronyms)
    {
        foreach (IMutableEntityType entity in modelBuilder.Model.GetEntityTypes())
        {
            var underscored = AddUndercoresToSentence(entity.DisplayName(), preserveAcronyms);
            entity.Relational().TableName = underscored.ToLower();
        }
    }
    private static string AddUndercoresToSentence(string text, bool preserveAcronyms)
    {
        if (string.IsNullOrWhiteSpace(text))
            return string.Empty;
        var newText = new StringBuilder(text.Length * 2);
        newText.Append(text[0]);
        for (int i = 1; i < text.Length; i++)
        {
            if (char.IsUpper(text[i]))
                if ((text[i - 1] != '_' && !char.IsUpper(text[i - 1])) ||
                    (preserveAcronyms && char.IsUpper(text[i - 1]) &&
                     i < text.Length - 1 && !char.IsUpper(text[i + 1])))
                    newText.Append('_');
            newText.Append(text[i]);
        }
        return newText.ToString();
    }

它还可以将缩略词:PrepareXXRecord转换为prepare_xx_record

TL&DR: SnakeCase解决方案不适用于身份框架;使用下面的方法手动创建它们;

说明: SnakeCase()函数适用于大多数问题。但是,有时此方法无法正确格式化数据库表。一个非常流行的例子是身份框架。在这些情况下,建议您手动命名表;

修复:

protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
            //Configure default schema
            base.OnModelCreating(modelBuilder);
            modelBuilder.Entity<IdentityUser>().ToTable("asp_net_users");
            modelBuilder.Entity<IdentityUserToken<string>>().ToTable("asp_net_user_tokens");
            modelBuilder.Entity<IdentityUserLogin<string>>().ToTable("asp_net_user_logins");
            modelBuilder.Entity<IdentityUserClaim<string>>().ToTable("asp_net_user_claims");
            modelBuilder.Entity<IdentityRole>().ToTable("asp_net_roles");
            modelBuilder.Entity<IdentityUserRole<string>>().ToTable("asp_net_user_roles");
            modelBuilder.Entity<IdentityRoleClaim<string>>().ToTable("asp_net_role_claims");
}

在EF Core 6中,您可以在ModelBuilder上使用以下扩展:

public static class ModelBuilderExtensions
{
    public static void ApplyNamingConvention(this ModelBuilder modelBuilder)
    {
        var modelEntityTypes = modelBuilder.Model.GetEntityTypes();
        foreach (var tableConfiguration in modelEntityTypes)
        {
            // Table Naming
            tableConfiguration.SetTableName(tableConfiguration.GetTableName().ToLowerUnderscoreName());
            // Column Naming
            var columnsProperties = tableConfiguration.GetProperties();
            foreach (var columnsProperty in columnsProperties)
            {
                var isOwnedProperty = columnsProperty.DeclaringEntityType.IsOwned();
                if (isOwnedProperty && columnsProperty.IsPrimaryKey())
                    continue;
                if (isOwnedProperty)
                {
                    var ownership = columnsProperty.DeclaringEntityType.FindOwnership();
                    var ownershipName = ownership.PrincipalToDependent.Name;
                    var columnName = $"{ownershipName}_{columnsProperty.Name}";
                    columnsProperty.SetColumnName(columnName.ToLowerUnderscoreName());
                }
                else
                {
                    columnsProperty.SetColumnName(columnsProperty.Name.ToLowerUnderscoreName());
                }
            }
            // Find primary key
            var pk = tableConfiguration.FindPrimaryKey();
            pk.SetName(pk.GetName().ToLowerUnderscoreName());
            // Foreign keys
            var fks = tableConfiguration.GetForeignKeys();
            foreach (var fk in fks)
            {
                var fkName = fk.GetConstraintName().ToLowerUnderscoreName();
                fk.SetConstraintName(fkName);
            }
            // Indexes
            var idxs = tableConfiguration.GetIndexes();
            foreach (var idx in idxs)
            {
                idx.SetDatabaseName(idx.GetDatabaseName().ToLowerUnderscoreName());
            }
        }
    }
        
    public static string ToLowerUnderscoreName(this string text)
    {
        if (string.IsNullOrEmpty(text))
        {
            return text;
        }
        var builder = new StringBuilder(text.Length + Math.Min(2, text.Length / 5));
        var previousCategory = default(UnicodeCategory?);
        for (var currentIndex = 0; currentIndex < text.Length; currentIndex++)
        {
            var currentChar = text[currentIndex];
            if (currentChar == '_')
            {
                builder.Append('_');
                previousCategory = null;
                continue;
            }
            var currentCategory = char.GetUnicodeCategory(currentChar);
            switch (currentCategory)
            {
                case UnicodeCategory.UppercaseLetter:
                case UnicodeCategory.TitlecaseLetter:
                    if (previousCategory == UnicodeCategory.SpaceSeparator ||
                            previousCategory == UnicodeCategory.LowercaseLetter ||
                            previousCategory != UnicodeCategory.DecimalDigitNumber &&
                            previousCategory != null &&
                            currentIndex > 0 &&
                            currentIndex + 1 < text.Length &&
                            char.IsLower(text[currentIndex + 1]))
                    {
                        builder.Append('_');
                    }
                    currentChar = char.ToLower(currentChar, CultureInfo.InvariantCulture);
                    break;
                case UnicodeCategory.LowercaseLetter:
                case UnicodeCategory.DecimalDigitNumber:
                    if (previousCategory == UnicodeCategory.SpaceSeparator)
                    {
                        builder.Append('_');
                    }
                    break;
                default:
                    if (previousCategory != null)
                    {
                        previousCategory = UnicodeCategory.SpaceSeparator;
                    }
                    continue;
            }
            builder.Append(currentChar);
            previousCategory = currentCategory;
        }
        return builder.ToString();
    }
}

DbContext中,我有:

    protected override void OnModelCreating(ModelBuilder builder)
    {
        builder.ApplyNamingConvention();
    }