.NET Core代码重构:移除GetById与Map方法中的Switch Case
重构方案:用策略模式+依赖注入移除Switch Case
针对你在.NET Core中要移除GetById和Map方法内switch case的需求,推荐用策略模式+依赖注入的方案,把不同身份提供商的逻辑封装成独立实现类,彻底消除分支判断,同时符合单一职责和开闭原则。
步骤1:定义基础抽象与实体
先统一用户基类和身份提供商枚举,让各厂商用户类继承基类,保证类型安全:
public enum IdentityProviderType { Okta, Ping, Internal } // 所有身份提供商用户的基类,包含公共属性 public abstract class IdpUser { public string Id { get; set; } public string Username { get; set; } public string Email { get; set; } } // 各厂商具体用户类(保留特有属性) public class OktaUser : IdpUser { public string OktaCustomAttribute { get; set; } } public class PingUser : IdpUser { public string PingSessionId { get; set; } } public class InternalUser : IdpUser { public string EmployeeId { get; set; } }
步骤2:拆分逻辑为独立策略接口
分别定义用户获取和映射的接口,每个厂商对应一个实现类:
用户获取接口与实现
public interface IIdpUserService { Task<IdpUser> GetByIdAsync(string userId); IdentityProviderType ProviderType { get; } // 标记当前服务对应厂商 } // Okta实现类 public class OktaUserService : IIdpUserService { private readonly IOktaClient _oktaClient; // 注入原有Okta客户端 public OktaUserService(IOktaClient oktaClient) { _oktaClient = oktaClient; } public IdentityProviderType ProviderType => IdentityProviderType.Okta; public async Task<IdpUser> GetByIdAsync(string userId) { var rawUser = await _oktaClient.GetUserById(userId); return new OktaUser { Id = rawUser.Id, Username = rawUser.Username, Email = rawUser.Email, OktaCustomAttribute = rawUser.Profile.CustomField }; } } // 同理实现PingUserService、InternalUserService...
用户映射接口与实现
假设你需要把厂商用户映射到统一的UserDto:
public class UserDto { public string Id { get; set; } public string Username { get; set; } public string Email { get; set; } public string ProviderSpecificInfo { get; set; } } public interface IUserMapper { UserDto Map(IdpUser user); IdentityProviderType ProviderType { get; } } // Okta映射实现 public class OktaUserMapper : IUserMapper { public IdentityProviderType ProviderType => IdentityProviderType.Okta; public UserDto Map(IdpUser user) { var oktaUser = (OktaUser)user; return new UserDto { Id = oktaUser.Id, Username = oktaUser.Username, Email = oktaUser.Email, ProviderSpecificInfo = oktaUser.OktaCustomAttribute }; } } // 同理实现PingUserMapper、InternalUserMapper...
步骤3:创建工厂类统一获取策略
用工厂类根据枚举快速找到对应服务,替代switch判断:
public class IdpServiceFactory { private readonly IEnumerable<IIdpUserService> _userServices; private readonly IEnumerable<IUserMapper> _userMappers; // 通过构造函数注入所有已注册的策略实现 public IdpServiceFactory(IEnumerable<IIdpUserService> userServices, IEnumerable<IUserMapper> userMappers) { _userServices = userServices; _userMappers = userMappers; } public IIdpUserService GetUserService(IdentityProviderType providerType) { return _userServices.FirstOrDefault(s => s.ProviderType == providerType) ?? throw new ArgumentException($"未找到{providerType}对应的用户服务"); } public IUserMapper GetUserMapper(IdentityProviderType providerType) { return _userMappers.FirstOrDefault(m => m.ProviderType == providerType) ?? throw new ArgumentException($"未找到{providerType}对应的映射器"); } }
步骤4:注册服务到DI容器
在Program.cs(或Startup.cs)中注册所有策略类和工厂:
// .NET 6+ 示例 builder.Services.AddScoped<IOktaClient, OktaClient>(); // 原有Okta客户端注册 // 注册所有用户服务实现 builder.Services.AddScoped<IIdpUserService, OktaUserService>(); builder.Services.AddScoped<IIdpUserService, PingUserService>(); builder.Services.AddScoped<IIdpUserService, InternalUserService>(); // 注册所有映射器实现 builder.Services.AddScoped<IUserMapper, OktaUserMapper>(); builder.Services.AddScoped<IUserMapper, PingUserMapper>(); builder.Services.AddScoped<IUserMapper, InternalUserMapper>(); // 注册工厂 builder.Services.AddScoped<IdpServiceFactory>();
步骤5:改造原有业务方法
注入工厂类,直接调用对应策略完成逻辑,彻底移除switch:
public class UserManager { private readonly IdpServiceFactory _factory; public UserManager(IdpServiceFactory factory) { _factory = factory; } public async Task<UserDto> GetUserAsync(string userId, IdentityProviderType providerType) { // 获取对应厂商的用户服务 var userService = _factory.GetUserService(providerType); var idpUser = await userService.GetByIdAsync(userId); // 获取对应厂商的映射器 var mapper = _factory.GetUserMapper(providerType); return mapper.Map(idpUser); } }
方案优势
- 无分支判断:完全移除switch case,代码更简洁易读
- 可扩展性:新增身份提供商时,只需添加对应的
IIdpUserService和IUserMapper实现,注册到DI即可,无需修改原有业务代码 - 单一职责:每个厂商的获取和映射逻辑独立封装,便于维护和测试
- 类型安全:用基类
IdpUser替代object,避免无意义的强制转换风险
内容的提问来源于stack exchange,提问作者los pollos hermanos
相关产品推荐
相关产品推荐

