繁体   English   中英

ASP.NET Core创建类可以在其中使用DI的类实例-寻找模式建议

[英]ASP.NET Core Create class instance where class may use DI - looking for pattern advice

我有一个问题,我需要使用一些配置数据实例化一个类,但是该类可能会使用它从DI容器中收集的其他类。 我在Startup中注册了许多服务,我可以从控制器和服务中Startup访问它们-运行良好。

问题是我有一个接口IProvider ,该接口有许多实现它的类ProviderAProviderB等。在运行时,根据用户的选择,我需要创建这些类之一的实例并在其上调用一些方法。

该类需要一些配置数据(用于与外部系统进行通信),我需要弄清楚如何传递配置数据,以及允许DI用于需要其他服务的那些类。

IProvider接口定义为:

public interface IProvider
{
    Task<string> ValidateAsync();
}

使用该类的类可能像这样:

public class ProviderA: IProvider
{
    private readonly SMSService _smsService;

    public ProviderA(SMSService smsService, string configuration)
    {
        _smsService = smsService;
        //do something with configuration
    }

    public Task<string> ValidateAsync()
    {
        //validate connection using passed in configuration
        throw new NotImplementedException();
    }
}

根据用户输入,我需要创建该类的新实例,但希望该类确定自己的依赖关系(在本例中为SMSService )。 我还注意到, SMSService可能还具有其他依赖项( dbContext等)。

我用于创建新实例的代码是确定对象的类型并创建实例:

provider = new ProviderA(configuration);

关于如何创建此对象的实例,传递参数并允许其获取自己的依赖关系的任何想法? 我怀疑我的方法是不正确的,但是在过去,它运行良好,但是现在使用DI会给我带来麻烦,因为需要获取各种服务并且需要传递参数。

我建议向IProvider接口添加一些描述字段,例如:

public interface IProvider
{
    Task<string> ValidateAsync();

    ProviderType Type { get; }
}

它允许您注入接口的所有实现,并根据用户输入选择其中之一:

public class MyService
{
    private IEnumerable<IProvider> providers;

    public MyService(IEnumerable<IProvider> providers)
    {
        this.providers = providers;
    }

    public Task Action(UserInput input)
    {
        var provider = providers.FirstOrDefault(el => el.Type == SmsProvider);
    }
}

至于配置,您可以为每种类型的Provider创建Config类并注册它:

public class Config
{
    public string SomeConfig { get; set; }
}

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton(new Config());
}

public ProviderA(SMSService smsService, Config configuration)
{

}

甚至将配置放在appsettings.json中并使用IOptions模式

public void ConfigureServices(IServiceCollection services)
{
    var section = Configuration.GetSection("ProviderA");
    services.Configure<Config>(section);
}

并食用:

public ProviderA(SMSService smsService, IOptions<Config> configuration)
{
    var config = configuration.Value;
}

这是工厂模式的理想方案。 注入工厂,然后在运行时从中获得所需的实际实例。 例如:

public class ProviderFactory
{
    public ProviderFactory(...) { ... } // Inject all the stuff you need here

    public IProvider CreateProvider(string type)
    {
        // switch on `type`, new up the right provider, and return it
    }
}

当然,您还可以在这里做其他事情。 您可能希望在构建过程中一次更新所有提供程序,或者可以使用Lazy<>来仅在访问它们时进行更新。 或者,您可以在创建实例时使用ConccurrentDictionary类的ConccurrentDictionary来存储实例。 这在很大程度上取决于您和应用程序的需求。 要点是拥有这个工厂类,您可以在其中提取所需的正确提供程序实例。 注入那个工厂,您就可以开始了。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM