我真的不喜欢在我的 PostgreSql 数据库中使用 PascalCase 标识符,因为我直接对数据库进行了大量手动查询,所以对于我的新 .NET Core 解决方案,我有点极端地改变它。
首先,我使用我的 PascalCase 实体类定义了我的标准 ApplicationDbContext 并将其标记为抽象,然后我专门为我的 Postgres 实现创建了一个 PgDbContext。
接下来,我创建了一个这样的辅助方法:
public static string FromPascalCaseToSnakeCase(this string str)
{
return string.IsNullOrWhiteSpace(str) ? str : string.Concat(str.Select((x, i) => i > 0 && char.IsUpper(x) ? "_" + x.ToString() : x.ToString())).ToLower();
}
然后我通过实现一些与 Npgsql 相关的类来覆盖一些关键方法:
public class LowercaseSqlGenerationHelper : RelationalSqlGenerationHelper
{
public LowercaseSqlGenerationHelper(RelationalSqlGenerationHelperDependencies dependencies) : base(dependencies)
{
}
public override void DelimitIdentifier(StringBuilder builder, string identifier)
{
base.DelimitIdentifier(builder, identifier.FromPascalCaseToSnakeCase());
}
public override void DelimitIdentifier(StringBuilder builder, string name, string schema)
{
base.DelimitIdentifier(builder, name.FromPascalCaseToSnakeCase(), schema.FromPascalCaseToSnakeCase());
}
public override string DelimitIdentifier(string identifier)
{
return base.DelimitIdentifier(identifier.FromPascalCaseToSnakeCase());
}
public override string DelimitIdentifier(string name, string schema)
{
return base.DelimitIdentifier(name.FromPascalCaseToSnakeCase(), schema.FromPascalCaseToSnakeCase());
}
}
public class LowercaseQuerySqlGenerator : NpgsqlQuerySqlGenerator
{
public LowercaseQuerySqlGenerator(QuerySqlGeneratorDependencies dependencies, RelationalSqlGenerationHelperDependencies rSGenDep, SelectExpression selectExpression) :
base(
new QuerySqlGeneratorDependencies(dependencies.CommandBuilderFactory,
new LowercaseSqlGenerationHelper(rSGenDep),
dependencies.ParameterNameGeneratorFactory,
dependencies.RelationalTypeMapper)
, selectExpression)
{
}
}
public class LowercaseHistoryRepository:NpgsqlHistoryRepository
{
public LowercaseHistoryRepository(HistoryRepositoryDependencies dependencies) : base(dependencies)
{
}
protected override string ExistsSql
{
get
{
var builder = new StringBuilder();
builder.Append("SELECT EXISTS (SELECT 1 FROM pg_catalog.pg_class c JOIN pg_catalog.pg_namespace n ON n.oid=c.relnamespace WHERE ");
if (TableSchema != null)
{
builder
.Append("n.nspname='")
.Append(SqlGenerationHelper.EscapeLiteral(TableSchema.FromPascalCaseToSnakeCase()))
.Append("' AND ");
}
builder
.Append("c.relname='")
.Append(SqlGenerationHelper.EscapeLiteral(TableName.FromPascalCaseToSnakeCase()))
.Append("');");
return builder.ToString();
}
}
}
最后,像这样连接IServiceCollection 配置:
services.AddDbContext<PgDbContext>(
options =>
{
options.UseNpgsql(config.GetSection("ConnectionStrings:ApplicationContext").Value)
.ReplaceService<ISqlGenerationHelper, LowercaseSqlGenerationHelper>()
.ReplaceService<IQuerySqlGenerator, LowercaseQuerySqlGenerator>()
.ReplaceService<IHistoryRepository, LowercaseHistoryRepository>();
},
ServiceLifetime.Scoped);
services.AddScoped<ApplicationDbContext>(di => di.GetService<PgDbContext>());
有了这个,我所有的表名、列和约束都以 snake_case 而不是 PascalCase 命名,这意味着我不必担心在手动查询中使用带引号的标识符。我的实体类按照我喜欢的方式大小写,我的数据库名称也是我喜欢的方式。
YMMV,但它对我来说非常有效。请务必注意,虽然这实际上并没有从 EF 查询中删除引号,但它使手动查询所需的引号消失了。