使用 Microsoft DI 注册多个复杂的依赖项

Ano*_*ni2 5 c# dependency-injection multiple-instances asp.net-core

我的 ASP.NET Core 应用程序使用由一组类(“Helper”、“Client”、“UserSetting”、“ServerSetting”)组成的软件客户端,从依赖项注入中检索其正确的配置/实例。单个类可以在应用程序中配置为单例并使用。但是我怎样才能拥有这组类的多个实例呢?现在“Client”和“Helper”的实例A如何收到A的相应设置,而实例B收到其不同的相应值?

我所拥有的:单个客户端有一个助手,并在 appsettiongs.json 中进行设置。助手和客户端都需要设置。一切都可以沟通

目标案例:“Client”、“Helper”、“UserOptions”、“ServerOptions”类的多个实例。如图所示,我需要此包类的多个实例来连接到多个服务器并与选定的服务器进行通信。每个客户端必须针对另一个服务器/用户数据组合进行配置。

当前单个客户端的初始化:

class Startup {
  IConfiguration configuration {get;}
  public Startup(){//init configuration from appsettings.json} 

  public void ConfigureServices(IServiceCollection services){
    ... 
    services.Configure<UserSettings>(Configuration.GetSection("userData");
    service.Configure<ServerSettings>(Configuration.GetSection("serverSettings");
    service.AddSingleton<IHelper, MyHelper>();
    service.AddSingleton<IClient, MyClient>();
  }
}

public class MyClient:IClient{
  public void MyClient(ILogger<MyClient> log, IHelper helper){..}
}

public class MyHelper:IHelper{
  public void MyHelper(ILogger<MyHelper> log, IOptions<UserSettings> userSettings, IOptions<ServerSettings> serverSettings){..}
}

class MyWebController{
  public IClient Client;

  public MyWebController(IClient client){ this.Client = client;}

  public ActionResult DoSomeServerStuff(string data){
     Client.Communicate();
  }
}
Run Code Online (Sandbox Code Playgroud)

首选用法的想法:

class Startup {
  IConfiguration configuration {get;}
  public Startup(){//init configuration from appsettings.json} 

  public void ConfigureServices(IServiceCollection services){
    { 
      //configure in some scope for A
      services.Configure<UserSettings>(Configuration.GetSection("ServerA:userData");
      service.Configure<ServerSettings>(Configuration.GetSection("ServerA:serverSettings");
      service.AddSingleton<IHelper, MyHelper>();
      service.AddSingleton<IClient, MyClient>();
    }
    {  
      //configure in some scope for B (How can I kind of scope it within those brackets?)
      services.Configure<UserSettings>(Configuration.GetSection("ServerB:userData");
      service.Configure<ServerSettings>(Configuration.GetSection("ServerB:serverSettings");
      service.AddSingleton<IHelper, MyHelper>();
      service.AddSingleton<IClient, MyClient>();
    }
  }
}

class MyWebController{
  public IClient ClientA, ClientB;

  public MyClass(Magic magic){
    ClientA = magic.GetClient("A");
    ClientB = magic.GetClient("B");
  }

  // GET: MyWebController/
  public ActionResult Index(string data){
     switch(data){
       case "A": 
         ServerA.Communicate();
         break;
       case "B":
         ServerB.Communicate();
         break;
     }
  }
Run Code Online (Sandbox Code Playgroud)

我尝试将初始化打包到容器中,以手动初始化它,但未能按照构造函数的要求创建对象 IOptions。UserSettings 值无法转换为 IOptons 值。

class MyContainer{
  public IClient ClientA, ClientB;
  public MyContainer(IConfiguration config, ILoggerFactory factory){
    userSettingsA = config.GetSection("ServerA:userData").Get<UserSettings>();
    serverSettingsA = config.GetSection("ServerA:serverSettings").Get<ServerSettings();
    var helperA = new MyHelper(factory.CreateLogger<MyHelper>, userSettingsA, serverSettingsA);
    ClientA = new MyClient(factory.CreateLogger<Client>(), helper);
    
    // ClientB configured analog to ClientA
  }
}
class Startup{
  public ConfigureServices(IServiceCollection services){
    service.AddSingleton(MyContainer);
  }
}
Run Code Online (Sandbox Code Playgroud)

但是,这会失败,因为作为 UserSettings 实例的 userSettingsA 不可转换为 IOptions。因此,即使采用不推荐的显式构建类,我也失败了,跳过了 DI。

关于如何继续的任何提示?关于如何在使用 DI 创建过程中确定对象范围或如何通过创建 IOptions 类型手动创建这些对象有什么想法吗?

Ano*_*ni2 0

我能够在上面的评论的帮助下修复它。由于我在这里使用单例,因此它有助于使用此 ServiceFactory 为每个客户端创建服务。在瞬态值等情况下,人们可能会遇到更多问题。但是,这对我有用:

New classes: 

public class IClientCollection {
  public IClient GetClient(string val);
}

public class ClientCollection {
  private IClient ClientA, ClientB;
  public ClientCollection (IClient a, IClient b){
    ClientA = a;
    ClientB = b;
  }

  public IClient GetClient(string val){
    switch (val){
      case "A": return ClientA;
      case "B": return ClientB;
  }
}
Run Code Online (Sandbox Code Playgroud)
Initialization in Startup.cs

class Startup {
  IConfiguration configuration {get;}

  public Startup() { /*init configuration from appsettings.json*/ } 

  public void ConfigureServices(IServiceCollection services){
...
    var serviceA = CreateClientService(Configuration.GetSection("ServerA:userData"), Configuration.GetSection("ServerA:serverSettings"));
    var serviceB = CreateClientService(Configuration.GetSection("ServerB:userData"), Configuration.GetSection("ServerB:serverSettings"));

    var clientCollection = new ClientCollection(
        serviceA.GetService<IClient>();
        serviceB.GetService<IClient>();
    );
    serivces.AddSingleton<IClientCollection>(clientCollection);
...
  }

    public ServiceProvider CreateClientService(IServiceCollection services, IConfigurationSection userSettings, IConfigurationSection serverSettings)
    {  
      newService = new ServiceCollection();
      newService.AddSingleton(services);

      newService.Configure<UserSettings>(userSettings);
      newService.Configure<ServerSettings>(serverSettings);
      newService.AddSingleton<IHelper, MyHelper>();
      newService.AddSingleton<IClient, MyClient>();
      
      return newService.BuildServiceProvider();
    }
  }
}
Run Code Online (Sandbox Code Playgroud)
Usage: 

class MyWebController{
  public IClient ClientA, ClientB;

  public MyClass(IClientCollection collection){
    ClientA = collection.GetClient("A");
    ClientB = collection.GetClient("B");
  }

  // GET: MyWebController/
  public ActionResult Index(string data){
     switch(data){
       case "A": 
         ClientA.Communicate();
         break;
       case "B":
         ClientB.Communicate();
         break;
     }
  }
Run Code Online (Sandbox Code Playgroud)

感谢您的支持@Jemery Lakeman。