接続文字列をいくつかのサービス実装に渡すことができる必要があります。これをコンストラクターで実行しています。接続文字列は、ユーザーがクレームとして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'.
ここで私のオプションは何ですか?
public void ConfigureServices(IServiceCollection services)
{
// Choose Scope, Singleton or Transient method
services.AddSingleton<IRootService, RootService>();
services.AddSingleton<INestedService, NestedService>(serviceProvider=>
{
return new NestedService("someConnectionString");
});
}
AppSettings.json内で接続文字列を非表示にすることにした場合、たとえば:
"Data": {
"ConnectionString": "someConnectionString"
}
その後、ConfigurationBuilder(通常はStartupクラスのコンストラクターにあります)にappSettings.jsonをロードした場合、ConfigureServicesは次のようになります。
public void ConfigureServices(IServiceCollection services)
{
// Choose Scope, Singleton or Transient method
services.AddSingleton<IRootService, RootService>();
services.AddSingleton<INestedService, NestedService>(serviceProvider=>
{
var connectionString = Configuration["Data:ConnectionString"];
return new NestedService(connectionString);
});
}
namespace Microsoft.Extensions.DependencyInjection
{
public static class RootServiceExtensions //you can pick a better name
{
//again pick a better name
public static IServiceCollection AddRootServices(this IServiceCollection services, string connectionString)
{
// Choose Scope, Singleton or Transient method
services.AddSingleton<IRootService, RootService>();
services.AddSingleton<INestedService, NestedService>(_ =>
new NestedService(connectionString));
}
}
}
次に、ConfigureServicesメソッドは次のようになります
public void ConfigureServices(IServiceCollection services)
{
var connectionString = Configuration["Data:ConnectionString"];
services.AddRootServices(connectionString);
}
さらにパラメーターが必要な場合は、さらに一歩進んで、RootServiceのコンストラクターに渡すオプションクラスを作成できます。複雑になる場合は、Builderパターンを使用できます。
アプリケーションの起動時に不明なランタイムパラメータを渡すには、ファクトリパターンを使用する必要があります。ここには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
}
}
工場クラス
public class RootServiceFactory : IRootServiceFactory
{
// in case you need other dependencies, that can be resolved by DI
private readonly IServiceCollection services;
public RootServiceCollection(IServiceCollection services)
{
this.services = services;
}
public CreateInstance(string connectionString)
{
// instantiate service that needs runtime parameter
var nestedService = new NestedService(connectionString);
// resolve another service that doesn't need runtime parameter
var otherDependency = services.GetService<IOtherService>()
// pass both into the RootService constructor and return it
return new RootService(otherDependency, nestedDependency);
}
}
IRootServiceFactory
の代わりにIRootService
を注入します。
IRootService rootService = rootServiceFactory.CreateIntsance(connectionString);