You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.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);
    }
}

方案优势

  1. 无分支判断:完全移除switch case,代码更简洁易读
  2. 可扩展性:新增身份提供商时,只需添加对应的IIdpUserService和IUserMapper实现,注册到DI即可,无需修改原有业务代码
  3. 单一职责:每个厂商的获取和映射逻辑独立封装,便于维护和测试
  4. 类型安全:用基类IdpUser替代object,避免无意义的强制转换风险

内容的提问来源于stack exchange,提问作者los pollos hermanos

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.06 14:45:55