如何按对应DbContext过滤批量注册的IEntityTypeConfiguration<>
实现方案
方案1:自定义特性标记(可控性最高,适合复杂场景)
这种方案可以精确控制每个配置类所属的DbContext,支持同一个实体对应不同DbContext使用不同配置的场景。
步骤1:定义关联特性
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)] public class EntityConfigurationForAttribute : Attribute { public Type TargetDbContextType { get; } public EntityConfigurationForAttribute(Type targetDbContextType) { TargetDbContextType = targetDbContextType; } }
步骤2:为配置类添加标记
// 标记该配置仅用于FirstDbContext [EntityConfigurationFor(typeof(FirstDbContext))] public class CustomerConfiguration : IEntityTypeConfiguration<Customer> { public void Configure(EntityTypeBuilder<Customer> builder) { builder.Property(p => p.Id).ValueGeneratedNever(); // 其余配置逻辑 } } // 标记该配置仅用于SecondDbContext [EntityConfigurationFor(typeof(SecondDbContext))] public class AccountConfiguration : IEntityTypeConfiguration<Account> { public void Configure(EntityTypeBuilder<Account> builder) { builder.Property(p => p.Id).ValueGeneratedNever(); // 其余配置逻辑 } }
步骤3:修改DbContext的配置加载逻辑
在OnModelCreating方法的ApplyConfigurationsFromAssembly中传入过滤规则:
public class FirstDbContext : DbContext { public DbSet<Customer> Customers { get; set; } public FirstDbContext (DbContextOptions<FirstDbContext> options) : base(options) { } protected override void OnModelCreating(ModelBuilder builder) { builder.ApplyConfigurationsFromAssembly( Assembly.GetExecutingAssembly(), // 仅加载标记为当前DbContext的配置 type => type.GetCustomAttribute<EntityConfigurationForAttribute>()?.TargetDbContextType == typeof(FirstDbContext) ); base.OnModelCreating(builder); } } public class SecondDbContext : DbContext { public DbSet<Account> Accounts { get; set; } public SecondDbContext (DbContextOptions<SecondDbContext> options) : base(options) { } protected override void OnModelCreating(ModelBuilder builder) { builder.ApplyConfigurationsFromAssembly( Assembly.GetExecutingAssembly(), // 仅加载标记为当前DbContext的配置 type => type.GetCustomAttribute<EntityConfigurationForAttribute>()?.TargetDbContextType == typeof(SecondDbContext) ); base.OnModelCreating(builder); } }
方案2:自动匹配DbSet(无需额外标记,适合常规场景)
如果你的实体仅属于唯一一个DbContext,可以用这个方案,无需添加任何标记,自动根据DbContext中声明的DbSet属性匹配对应的配置。
修改后的DbContext代码如下:
public class FirstDbContext : DbContext { public DbSet<Customer> Customers { get; set; } public FirstDbContext (DbContextOptions<FirstDbContext> options) : base(options) { } protected override void OnModelCreating(ModelBuilder builder) { // 读取当前DbContext所有DbSet对应的实体类型 var allowedEntityTypes = GetType().GetProperties() .Where(p => p.PropertyType.IsGenericType && p.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>)) .Select(p => p.PropertyType.GetGenericArguments()[0]) .ToHashSet(); builder.ApplyConfigurationsFromAssembly( Assembly.GetExecutingAssembly(), type => { // 校验当前类型是否为实体配置类 var configInterface = type.GetInterfaces().FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>)); if (configInterface == null) return false; // 读取配置对应的实体类型,判断是否属于当前DbContext var entityType = configInterface.GetGenericArguments()[0]; return allowedEntityTypes.Contains(entityType); } ); base.OnModelCreating(builder); } } public class SecondDbContext : DbContext { public DbSet<Account> Accounts { get; set; } public SecondDbContext (DbContextOptions<SecondDbContext> options) : base(options) { } protected override void OnModelCreating(ModelBuilder builder) { // 读取当前DbContext所有DbSet对应的实体类型 var allowedEntityTypes = GetType().GetProperties() .Where(p => p.PropertyType.IsGenericType && p.PropertyType.GetGenericTypeDefinition() == typeof(DbSet<>)) .Select(p => p.PropertyType.GetGenericArguments()[0]) .ToHashSet(); builder.ApplyConfigurationsFromAssembly( Assembly.GetExecutingAssembly(), type => { // 校验当前类型是否为实体配置类 var configInterface = type.GetInterfaces().FirstOrDefault(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEntityTypeConfiguration<>)); if (configInterface == null) return false; // 读取配置对应的实体类型,判断是否属于当前DbContext var entityType = configInterface.GetGenericArguments()[0]; return allowedEntityTypes.Contains(entityType); } ); base.OnModelCreating(builder); } }
如果想复用过滤逻辑,也可以把这段逻辑封装为DbContext的扩展方法,减少重复代码。
内容的提问来源于stack exchange,提问作者ecasper
相关产品推荐
相关产品推荐

