我有一个问题,我需要使用一些配置数据实例化一个类,但是该类可能会使用它从DI容器中收集的其他类。
我在Startup
中注册了许多服务,我可以从控制器和服务访问它们而不会出现任何问题-运行良好。
问题是我有一个接口IProvider
,该接口有许多实现它的类ProviderA
,ProviderB
等。在运行时,根据用户的选择,我需要创建这些类之一的实例并在其上调用一些方法。
该类需要一些配置数据(用于与外部系统通信),我需要弄清楚如何传递配置数据,以及允许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却给我带来了麻烦,因为需要获取各种服务,并且需要传递参数。
答案 0 :(得分:0)
我建议在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 pattern
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;
}
答案 1 :(得分:0)
这是工厂模式的理想方案。注入工厂,然后在运行时从中获得所需的实际实例。例如:
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
之类的东西来存储实例。这在很大程度上取决于您和应用程序的需求。要点是拥有这个工厂类,您可以在其中提取所需的正确提供程序实例。注入那家工厂,你就好了。