.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')]
来更改生成的表名。
但是,我需要的是一个方法来做它在全局的方式。
我知道这个问题很老了,已经有答案了,但是这个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();
}