简体   繁体   English

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

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

I have an issue where I need to instantiate a class with some configuration data but that class may use other classes that it gathers from the DI container. 我有一个问题,我需要使用一些配置数据实例化一个类,但是该类可能会使用它从DI容器中收集的其他类。 I have many services registered in Startup that I can access from controllers and services with no issues - working well. 我在Startup中注册了许多服务,我可以从控制器和服务中Startup访问它们-运行良好。

The problem is I have an interface, IProvider , that has a number of classes that implement it ProviderA , ProviderB etc. At runtime, based on the users choice, I need to create an instance of one of these classes and call some methods on it. 问题是我有一个接口IProvider ,该接口有许多实现它的类ProviderAProviderB等。在运行时,根据用户的选择,我需要创建这些类之一的实例并在其上调用一些方法。

The class requires some configuration data (they are used to communicate with external systems) and I need to work out how to pass the config data as well as allow the DI to work for those classes that require some other services. 该类需要一些配置数据(用于与外部系统进行通信),我需要弄清楚如何传递配置数据,以及允许DI用于需要其他服务的那些类。

The IProvider interface is defined as: IProvider接口定义为:

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

A class that uses this may be like this: 使用该类的类可能像这样:

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

Based on user input I need to create a new instance of the class but want the class to work out its own dependencies ( SMSService in this case). 根据用户输入,我需要创建该类的新实例,但希望该类确定自己的依赖关系(在本例中为SMSService )。 I also note that SMSService may also have other dependencies ( dbContext etc). 我还注意到, SMSService可能还具有其他依赖项( dbContext等)。

My code for creating the new instance is to work out the type of the object and to create an instance: 我用于创建新实例的代码是确定对象的类型并创建实例:

provider = new ProviderA(configuration);

Any ideas on how do I create an instance of this object, pass in params, and allow it to obtain its own dependencies? 关于如何创建此对象的实例,传递参数并允许其获取自己的依赖关系的任何想法? I suspect that my approach is incorrect but in the past, it has worked well but now with DI it is giving me trouble as the various services need to be sourced AND the params need to be passed. 我怀疑我的方法是不正确的,但是在过去,它运行良好,但是现在使用DI会给我带来麻烦,因为需要获取各种服务并且需要传递参数。

I would suggest add some description field to IProvider interface, eg: 我建议向IProvider接口添加一些描述字段,例如:

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

    ProviderType Type { get; }
}

It allows you to inject all implementation of the interface and select one of them based on user input: 它允许您注入接口的所有实现,并根据用户输入选择其中之一:

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

As for configuration, you can create Config class for each type of Provider and register it: 至于配置,您可以为每种类型的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)
{

}

Or even put the config in appsettings.json and use IOptions pattern 甚至将配置放在appsettings.json中并使用IOptions模式

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

and consume it: 并食用:

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

This is a perfect scenario for a factory pattern. 这是工厂模式的理想方案。 You inject your factory, and then at runtime, you get the actual instance you need from that. 注入工厂,然后在运行时从中获得所需的实际实例。 For example: 例如:

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
    }
}

There's of course other things you can do here. 当然,您还可以在这里做其他事情。 You might want to new up all your providers once during construction, or you can use something like Lazy<> to new them up only when you access them. 您可能希望在构建过程中一次更新所有提供程序,或者可以使用Lazy<>来仅在访问它们时进行更新。 Or, you can use something like a ConccurrentDictionary to store instances as you create them. 或者,您可以在创建实例时使用ConccurrentDictionary类的ConccurrentDictionary来存储实例。 It's largely up to you and the needs of your application. 这在很大程度上取决于您和应用程序的需求。 The main point is to have this factory class where you can pull out the right provider instance you need. 要点是拥有这个工厂类,您可以在其中提取所需的正确提供程序实例。 Inject that factory and you're good to go. 注入那个工厂,您就可以开始了。

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

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