EF Core:不为各数据库单独创建IEntityTypeConfiguration类的替代方案
问题
我为每个实体单独提取了IEntityTypeConfiguration<>配置类,有时需要针对不同数据库类型使用不同配置。例如:
PostgreSQL配置:
public class PostConfigurationPostgres : IEntityTypeConfiguration<Post> { public void Configure(EntityTypeBuilder<Post> builder) { builder.Property(p => p.Created) .HasDefaultValueSql("now()"); } }
SQL Server配置:
public class PostConfigurationSqlServer : IEntityTypeConfiguration<Post> { public void Configure(EntityTypeBuilder<Post> builder) { builder.Property(p => p.Created) .HasDefaultValueSql("getdate()"); } }
我的DbContext代码:
public class KnowDbContext : DbContext { public KnowDbContext(DbContextOptions<KnowDbContext> options) : base(options){} public DbSet<Post>? Posts { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { var provider = this.Database.ProviderName; if (provider == "Npgsql.EntityFrameworkCore.PostgreSQL") modelBuilder.ApplyConfigurationsFromAssembly(typeof(KnowDbContext).Assembly, t => t.Name.Contains("Postgres")); else if (provider == "Microsoft.EntityFrameworkCore.SqlServer") modelBuilder.ApplyConfigurationsFromAssembly(typeof(KnowDbContext).Assembly, t => t.Name.Contains("SqlServer")); } }
请问是否存在无需为每种数据库创建PostConfigurationSqlServer、PostConfigurationPostgres这类IEntityTypeConfiguration<>实现类的多数据库模型配置替代方案?
替代方案
方案1:单配置类内判断数据库提供程序
把数据库特定配置逻辑合并到同一个IEntityTypeConfiguration实现类中,通过模型获取当前数据库提供程序后分支处理:
public class PostConfiguration : IEntityTypeConfiguration<Post> { public void Configure(EntityTypeBuilder<Post> builder) { // 通用配置写在这里 builder.HasKey(p => p.Id); // 数据库专属配置 var provider = builder.Metadata.Model.GetDatabaseProvider(); if (provider == "Npgsql.EntityFrameworkCore.PostgreSQL") { builder.Property(p => p.Created) .HasDefaultValueSql("now()"); } else if (provider == "Microsoft.EntityFrameworkCore.SqlServer") { builder.Property(p => p.Created) .HasDefaultValueSql("getdate()"); } } }
对应的DbContext简化为:
protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.ApplyConfigurationsFromAssembly(typeof(KnowDbContext).Assembly); }
方案2:封装数据库专属扩展方法
把重复的数据库判断逻辑封装成扩展方法,在配置类中直接调用,减少冗余代码:
public static class EntityTypeBuilderExtensions { public static PropertyBuilder<DateTime> HasDefaultCreationDate(this PropertyBuilder<DateTime> builder, string providerName) { return providerName switch { "Npgsql.EntityFrameworkCore.PostgreSQL" => builder.HasDefaultValueSql("now()"), "Microsoft.EntityFrameworkCore.SqlServer" => builder.HasDefaultValueSql("getdate()"), _ => builder }; } }
配置类中使用:
public class PostConfiguration : IEntityTypeConfiguration<Post> { public void Configure(EntityTypeBuilder<Post> builder) { var provider = builder.Metadata.Model.GetDatabaseProvider(); builder.Property(p => p.Created) .HasDefaultCreationDate(provider); } }
方案3:全局模型拦截器统一配置
如果有大量实体需要统一的数据库特定规则,可以用EF Core的模型拦截器全局处理:
public class DatabaseSpecificConfigurationInterceptor : IMutableModelInterceptor { public void OnModelCreating(ModelCreatingEventData eventData, InterceptionResult result) { var model = eventData.Model; var provider = model.GetDatabaseProvider(); // 遍历所有实体,对名为Created的DateTime字段统一配置默认值 foreach (var entityType in model.GetEntityTypes()) { var createdProperty = entityType.FindProperty("Created"); if (createdProperty != null && createdProperty.ClrType == typeof(DateTime)) { if (provider == "Npgsql.EntityFrameworkCore.PostgreSQL") { createdProperty.SetDefaultValueSql("now()"); } else if (provider == "Microsoft.EntityFrameworkCore.SqlServer") { createdProperty.SetDefaultValueSql("getdate()"); } } } } }
在DbContext中注册拦截器:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.AddInterceptors(new DatabaseSpecificConfigurationInterceptor()); }
内容的提问来源于stack exchange,提问作者athene
相关产品推荐
相关产品推荐

