如何将多组重复的DbSet批量插入代码重构为统一执行逻辑?
重构IdentityServer配置数据插入的重复逻辑
问题背景
当前代码中存在多组重复的if判断+foreach循环逻辑,用于将Config中的模型数据转换为实体后插入到configContext对应的DbSet中,每组逻辑仅针对的集合类型不同,需重构为统一执行逻辑以消除重复代码。
类型说明
configContext下的实体集合
configContext!.Clients:Duende.IdentityServer.EntityFramework.Entities.ClientconfigContext!.IdentityResources:Duende.IdentityServer.EntityFramework.Entities.IdentityResourceconfigContext!.ApiScopes:Duende.IdentityServer.EntityFramework.Entities.ApiScopeconfigContext!.ApiResources:Duende.IdentityServer.EntityFramework.Entities.ApiResource
Config下的模型集合
Config.Clients:Duende.IdentityServer.Models.ClientConfig.IdentityResources:Duende.IdentityServer.Models.IdentityResourceConfig.ApiScopes:Duende.IdentityServer.Models.ApiScopeConfig.ApiResources:Duende.IdentityServer.Models.ApiResource
原重复代码
// 向数据库添加OAuth2和OpenID Connect客户端 if (configContext!.Clients.Any() == false) { foreach (Client client in Config.Clients) { configContext.Clients.Add(client.ToEntity()); } configContext.SaveChanges(); } // 向数据库添加用户相关信息(如邮箱、用户ID等) if (configContext.IdentityResources.Any() == false) { foreach (IdentityResource resource in Config.IdentityResources) { configContext.IdentityResources.Add(resource.ToEntity()); } configContext.SaveChanges(); } // 向数据库添加API访问范围 if (configContext.ApiScopes.Any() == false) { foreach (ApiScope apiAccess in Config.ApiScopes) { configContext.ApiScopes.Add(apiAccess.ToEntity()); } configContext.SaveChanges(); } // 向数据库添加需保护的API if (configContext.ApiResources.Any() == false) { foreach (ApiResource api in Config.ApiResources) { configContext.ApiResources.Add(api.ToEntity()); } configContext.SaveChanges(); }
重构方案
方案1:封装泛型处理方法(推荐,类型安全)
通过泛型方法抽离所有重复逻辑,仅需传入目标DbSet、源模型集合以及模型转实体的委托即可:
/// <summary> /// 批量插入配置数据到指定DbSet /// </summary> /// <typeparam name="TModel">配置模型类型</typeparam> /// <typeparam name="TEntity">数据库实体类型</typeparam> /// <param name="dbSet">目标DbSet</param> /// <param name="models">源模型集合</param> /// <param name="convertToEntity">模型转实体的转换方法</param> private void SeedConfigData<TModel, TEntity>(DbSet<TEntity> dbSet, IEnumerable<TModel> models, Func<TModel, TEntity> convertToEntity) where TEntity : class { if (!dbSet.Any()) { foreach (var model in models) { dbSet.Add(convertToEntity(model)); } configContext.SaveChanges(); } }
调用时只需传入对应参数:
SeedConfigData(configContext.Clients, Config.Clients, model => model.ToEntity()); SeedConfigData(configContext.IdentityResources, Config.IdentityResources, model => model.ToEntity()); SeedConfigData(configContext.ApiScopes, Config.ApiScopes, model => model.ToEntity()); SeedConfigData(configContext.ApiResources, Config.ApiResources, model => model.ToEntity());
方案2:统一遍历映射集合(适合批量扩展场景)
若后续可能新增更多配置类型,可将所有映射关系整理为集合后统一遍历处理:
// 定义所有配置项的映射关系 var seedMappings = new List<(DbSet<object> EntitySet, IEnumerable<object> Models, Func<object, object> Converter)> { (configContext.Clients.Cast<object>(), Config.Clients.Cast<object>(), m => ((Duende.IdentityServer.Models.Client)m).ToEntity()), (configContext.IdentityResources.Cast<object>(), Config.IdentityResources.Cast<object>(), m => ((Duende.IdentityServer.Models.IdentityResource)m).ToEntity()), (configContext.ApiScopes.Cast<object>(), Config.ApiScopes.Cast<object>(), m => ((Duende.IdentityServer.Models.ApiScope)m).ToEntity()), (configContext.ApiResources.Cast<object>(), Config.ApiResources.Cast<object>(), m => ((Duende.IdentityServer.Models.ApiResource)m).ToEntity()) }; // 统一执行插入逻辑 foreach (var mapping in seedMappings) { if (!mapping.EntitySet.Any()) { foreach (var model in mapping.Models) { mapping.EntitySet.Add(mapping.Converter(model)); } configContext.SaveChanges(); } }
方案说明
- 方案1利用泛型的类型约束保证类型安全,代码可读性高,维护成本低,适合当前场景。
- 方案2通过统一管理映射关系,后续新增配置类型时仅需添加映射项即可,扩展性更强,但牺牲了部分类型安全性。
内容的提问来源于stack exchange,提问作者user20291437
相关产品推荐
相关产品推荐

