1. ホーム
  2. c#

依存性解決の一部としてランタイムパラメータを渡すにはどうすればよいですか?

2023-11-20 21:30:41

質問

私のサービスの実装の一部に接続文字列を渡すことができるようにする必要があります。私はこれをコンストラクタで行っています。接続文字列はユーザが設定可能で、ClaimとしてClaimsPrincipalに追加されます。

今のところ問題ありません。

残念ながら、ASP.NET Coreの依存性注入機能をフルに使って、DIでサービス実装を解決できるようにしたいとも思っています。

POCインプリメンテーションがあるのですが。

public interface IRootService
{
    INestedService NestedService { get; set; }

    void DoSomething();
}

public class RootService : IRootService
{
    public INestedService NestedService { get; set; }

    public RootService(INestedService nestedService)
    {
        NestedService = nestedService;
    }

    public void DoSomething()
    {
        // implement
    }
}


public interface INestedService
{
    string ConnectionString { get; set; }

    void DoSomethingElse();
}

public class NestedService : INestedService
{
    public string ConnectionString { get; set; }

    public NestedService(string connectionString)
    {
        ConnectionString = connectionString;
    }

    public void DoSomethingElse()
    {
        // implement
    }
}

これらのサービスは、起動時に登録され INestedService はコントローラのコンストラクタに追加されます。

public HomeController(INestedService nestedService)
{
    NestedService = nestedService;
}

予想通り、エラーが発生しました。 Unable to resolve service for type 'System.String' while attempting to activate 'Test.Dependency.Services.NestedService'.

ここで私のオプションは何ですか?

どのように解決するのですか?

アプリケーションの開始時に知られていない実行時パラメータを渡すには、ファクトリーパターンを使用する必要があります。ここでは2つのオプションがあります。

  1. ファクトリクラス (どのように IHttpClientFactory が実装されています)

     public class RootService : IRootService
     {
         public RootService(INestedService nested, IOtherService other)
         {
             // ...
         }
     }
    
     public class RootServiceFactory : IRootServiceFactory 
     {
         // in case you need other dependencies, that can be resolved by DI
         private readonly IServiceProvider services;
    
         public RootServiceFactory(IServiceProvider services)
         {
             this.services = services;
         }
    
         public IRootService CreateInstance(string connectionString) 
         {
             // instantiate service that needs runtime parameter
             var nestedService = new NestedService(connectionString);
    
             // note that in this example, RootService also has a dependency on
             // IOtherService - ActivatorUtilities.CreateInstance will automagically
             // resolve that dependency, and any others not explicitly provided, from
             // the specified IServiceProvider
             return ActivatorUtilities.CreateInstance<RootService>(services,
                 new object[] { nestedService, });
         }
     }
    
    

    を注入し IRootServiceFactory の代わりに IRootService

     IRootService rootService = rootServiceFactory.CreateInstance(connectionString);
    
    
  2. ファクトリーメソッド

     services.AddTransient<Func<string,INestedService>>((provider) => 
     {
         return new Func<string,INestedService>( 
             (connectionString) => new NestedService(connectionString)
         );
     });
    
    

    の代わりに、ファクトリーメソッドをサービスに注入してください。 INestedService

     public class RootService : IRootService
     {
         public INestedService NestedService { get; set; }
    
         public RootService(Func<string,INestedService> nestedServiceFactory)
         {
             NestedService = nestedServiceFactory("ConnectionStringHere");
         }
    
         public void DoSomething()
         {
             // implement
         }
     }
    
    

    または呼び出しごとに解決する

     public class RootService : IRootService
     {
         public Func<string,INestedService> NestedServiceFactory { get; set; }
    
         public RootService(Func<string,INestedService> nestedServiceFactory)
         {
             NestedServiceFactory = nestedServiceFactory;
         }
    
         public void DoSomething(string connectionString)
         {
             var nestedService = nestedServiceFactory(connectionString);
    
             // implement
         }
     }