.net 拆下具有不同物体的开关盒

9o685dep  于 2022-12-30  发布在  .NET
关注(0)|答案(4)|浏览(123)

我想删除GetByIdMap方法中的switch case块,以提高代码可读性,并简化.NET核心应用程序中的编码实践。

public enum IdentityProviderType
{
    Okta = 0,
    Ping = 1,
    Internal = 2
}

public async Task<UserResponseDTO> GetById(string id)
{
    IdentityProviderType provider = //custom logic to retrieve IdentityProviderType enum

    object idpUser = null;
    switch (provider)
    {
        case IdentityProviderType.Okta:
            idpUser = await _oktaUserService.GetById(id);
            break;
        case IdentityProviderType.Ping:
            idpUser = await _pingUserService.GetById(id);
            break;
        case IdentityProviderType.Internal:
            idpUser = await _internalUserService.GetById(id);
            break;
    }
    
    return _mapUserService.Map(idpUser);
}

public class MapUserService
{
    public UserResponseDTO Map(IdentityProviderType provider, object idpUser)
    {
        switch (provider)
        {
            case IdentityProviderType.Okta:
                var oktaUser = (OktaUser)idpUser;
                //Map OktaUser fields to UserResponseDTO fields
                break;
            case IdentityProviderType.Ping:
                var pingUser = (PingUser)idpUser;
                //Map PingUser fields to UserResponseDTO fields
                break;
        case IdentityProviderType.Internal:
                var internalUser = (InternalUser)idpUser;
                //Map InternalUser fields to UserResponseDTO fields
                break;
        }   
    }
}

从互联网上关于同一主题的不同资源中,我得出了可能的想法,但不确定如何实现它,因为我有不同的对象OktaUser/PingUser/InternalUser。
我开始创造一些

public abstract class IdpUserService
{
    public abstract object GetById(string id);
}

但不确定如何完成它,我应该使用T或对象作为输出吗?
任何帮助如何重构和摆脱开关情况将是有帮助的。

hvvq6cgz

hvvq6cgz1#

下面是可以帮助您的完整代码。
首先,您需要为方法GetById抽象IdpUserService

public abstract class IdpUserService
{
    public abstract IdpUser GetById(string id);
}

此外,您还需要抽象IdpUser并将方法Map移到其中。

public abstract class IdpUser
{
    public abstract UserResponseDTO Map();
}

现在您可以实现IdpUserService的特定版本。

public class OktaUserService : IdpUserService
{
    public override IdpUser GetById(string id)
    {
        return new OktaUser();
    }
}

public class PingUserService : IdpUserService
{
    public override IdpUser GetById(string id)
    {
        return new PingUser();
    }
}

public class InternalUserService : IdpUserService
{
    public override IdpUser GetById(string id)
    {
        return new InternalUser();
    }
}

类似地,您将需要实现IdpUser的特定版本以从特定服务类返回。

public class OktaUser:IdpUser
{
    public override UserResponseDTO Map()
    {
        return new UserResponseDTO();
    }
}

public class PingUser:IdpUser
{
    public override UserResponseDTO Map()
    {
        return new UserResponseDTO();
    }
}

public class InternalUser:IdpUser
{
    public override UserResponseDTO Map()
    {
        return new UserResponseDTO();
    }
}

现在您有了IdpUserService的多个实现,您希望使用枚举IdentityProviderType从中选择一个。
我们将使用依赖注入来构造对象。因此,我们将把所有服务注册到DI容器。并且创建一个类NamedImpl来将服务Map到特定的IdentityProviderType

public static void Main(string[] args)
    {
        using IHost host = Host.CreateDefaultBuilder(args)
            .ConfigureServices((_, services) =>
            {
                services.AddSingleton<OktaUserService>();
                services.AddSingleton<PingUserService>();
                services.AddSingleton<InternalUserService>();
                services.AddSingleton<IdpUserServiceFactory>();
                services.AddSingleton<NamedImpl<IdpUserService>>(_ =>
                {
                    return new NamedImpl<IdpUserService>(Test2.IdentityProviderType.Okta.ToString(),
                        _.GetService<OktaUserService>());
                });
                services.AddSingleton<NamedImpl<IdpUserService>>(_ =>
                {
                    return new NamedImpl<IdpUserService>(Test2.IdentityProviderType.Internal.ToString(),
                        _.GetService<InternalUserService>());
                });
                services.AddSingleton<NamedImpl<IdpUserService>>(_ =>
                {
                    return new NamedImpl<IdpUserService>(Test2.IdentityProviderType.Ping.ToString(),
                        _.GetService<PingUserService>());
                });
                services.AddSingleton<Test2>();
            })
            .Build();
        var test2 = host.Services.GetService<Test2>();
        test2.GetById("");
        host.RunAsync().Wait();
    }

现在,为了获取特定的IdpUserService,我们将创建一个工厂类,通过构造函数获取所有已注册IdpUserService类型的列表。以及一个get方法,获取IdentityProviderType作为参数,并返回IdentityProviderType所提供参数的特定实现的IdpUserService

public class IdpUserServiceFactory
{
    private readonly List<NamedImpl<IdpUserService>> idpUserServices;

    public IdpUserServiceFactory(List<NamedImpl<IdpUserService>> idpUserServices)
    {
        this.idpUserServices = idpUserServices;
    }

    public IdpUserService Get(Test2.IdentityProviderType type)
    {
        return idpUserServices.Where(P => P.Name == type.ToString()).FirstOrDefault()?.Impl;
    }
}

现在,您所发布的代码的问题,可以结构如下,以利用所有的变化,我们已经做了以上。

public class Test2
{
    private readonly IdpUserServiceFactory idpUserServiceFactory;

    public Test2(IdpUserServiceFactory idpUserServiceFactory)
    {
        this.idpUserServiceFactory = idpUserServiceFactory;
    }

    public enum IdentityProviderType
    {
        Okta = 0,
        Ping = 1,
        Internal = 2
    }

    public async Task<UserResponseDTO> GetById(string id)
    {
        IdentityProviderType
            provider = IdentityProviderType.Internal; //custom logic to retrieve IdentityProviderType enum
        IdpUserService idpUserService = this.idpUserServiceFactory.Get(provider);
        IdpUser idpUser = idpUserService.GetById(id);
        return idpUser.Map();
    }
}

我知道这看起来太复杂了,但是这样你的代码将是非常可扩展的,因为你正在为同一类型的服务使用许多实现。
针对NamedImpl的实施

public class NamedImpl<T>
{
    public string Name { get; private set; }
    public T Impl { get; private set; }

    public NamedImpl(string name, T impl)
    {
        Name = name;
        Impl = impl;
    }
}
vuktfyat

vuktfyat2#

也许只有一个具有不同属性的用户对象,而不是OktaUserPingUserInternalUser

k0pti3hp

k0pti3hp3#

如果不全面了解代码库,很难选择最佳选项,根据所提供的代码,最佳选项似乎是抽象IdpUserService,这样它将返回UserResponseDTO,并在具体的服务实现中处理转换:

public abstract class IdpUserService
{
    public abstract UserResponseDTO GetById(string id);
}

如果有部分代码需要使用“原始”用户,则为每个类型创建Map方法(扩展方法或作为%SourceName%User类型的一部分):

public async Task<UserResponseDTO> GetById(string id)
{
    IdentityProviderType provider = //custom logic to retrieve IdentityProviderType enum
    UserResponseDTO idpUser = provider switch
    {
        IdentityProviderType.Okta => (await _oktaUserService.GetById(id)).ToUserDto(),
        IdentityProviderType.Ping => (await _pingUserService.GetById(id)).ToUserDto(), 
        ...
        _ => throw new ArgumentOutOfRangeException()
    };
    return idpUser;
}
fv2wmkja

fv2wmkja4#

大概是这样的

public interface IIdentityProvider {

    Task<UserResponseDTO> GetById(string id);

    UserResponseDTO Map(object idpUser);

}

public static class ProviderFactory {

    public static IIdentityProvider GetIdentityProvider(IdentityProviderType providerType) {
        switch (providerType) {
            case IdentityProviderType.Okta:
                return OktaUserService.Singleton;
            case IdentityProviderType.Ping:
                return PingUserService.Singleton;
            case IdentityProviderType.Internal:
                return InternalUserService.Singleton;
        }
        throw new NotSupportedException($"{nameof(IdentityProviderType)} '{providerType}' is not supported!");
    }

}

相关问题