.NET Core实体框架(EF Core)表命名约定

11

.net core实体框架(EF Core)表命名约定单数/简单/下划线

作为单一下划线表命名的粉丝,我对EF Core使用复数PascalCase来命名表感到不舒服。

模型

public class SourceType {
   ... 

DbContext

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

这将创建一个名为SourceTypes的表格(帕斯卡命名法和复数形式)

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

但是,我需要一种全局方式来实现它。


2
最新的EF Core修订版使用在上下文中使用的名称作为您的表名,因此如果您编写DbSet<SourceType> MyTableName,则您的表将不会被称为Sourcetypes,而是将被称为MyTableName。虽然这不是您想要的自动化,但了解这一点很方便。 - Lonefish
5个回答

14
我知道这个问题很老并且已经有答案了,但是这个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

你是绝对正确的,@AndreaBlengino。 - Hamid Mayeli

7

简述

使用扩展方法扩展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();
    }
}

我希望这篇文章能够帮助像我一样的开发者,不再浪费时间寻找解决方案。 :)


1

简述:SnakeCase解决方案不适用于Identity Framework;请使用以下方法手动创建。

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

修复方法

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");
}

1

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

0
在 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();
    }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接