ホームページ  >  記事  >  バックエンド開発  >  ASP.NET Core アプリケーションでのサードパーティ IoC/DI フレームワークとの統合

ASP.NET Core アプリケーションでのサードパーティ IoC/DI フレームワークとの統合

巴扎黑
巴扎黑オリジナル
2017-04-17 10:44:521643ブラウズ

ASP.NET Core アプリケーションでは、スタートアップの種類を定義する ConfigureServices メソッドで ServiceProvider を返すことによって、サードパーティの DI フレームワークの統合を実現できます。しかし、それはそれほど簡単ではありません。例を通して説明しましょう

1. ConfigureServices メソッドによって返される ServiceProvider は役に立ちません

この問題を簡単な例で説明します。まず、次の MyServiceProvider を定義します。これは、実際には別の ServiceProvider のカプセル化です。簡単にするために、サービス インターフェイスと実装タイプの間のマッピング関係を保存するためにディクショナリを使用します。この関係は、Register メソッドを呼び出すことで登録できます。サービス インスタンスを提供する GetService メソッドでは、提供されたサービスの種類が登録されている場合は、対応するインスタンス オブジェクトを作成して返します。それ以外の場合は、カプセル化された ServiceProvider を使用してサービスを提供します。サービス インスタンスを正常にリサイクルできるようにするために、サービス タイプが IDisposable インターフェイスを実装している場合、それをフィールド _disposables で表されるコレクションに追加します。 MyServiceProvider の Dispose メソッドが呼び出されると、これらの提供されたサービス インスタンスの Dispose メソッドが呼び出されます。


 public class MyServiceProvider : IServiceProvider, IDisposable
 {
 private IServiceProvider _innerServiceProvider;
 private Dictionary<Type, Type> _services;
 private List<IDisposable> _disposables;
 
 public MyServiceProvider(IServiceProvider innerServiceProvider)
 {
 _innerServiceProvider = innerServiceProvider;
 this._services = new Dictionary<Type, Type>();
 _disposables = new List<IDisposable>();
 }
 
 
 public MyServiceProvider Register<TFrom, TTo>() where TTo: TFrom, new()
 {
 _services[typeof(TFrom)] = typeof(TTo);
 return this;
 }
 
 public object GetService(Type serviceType)
 {
 Type implementation;
 if (_services.TryGetValue(serviceType, out implementation))
 {
 object service = Activator.CreateInstance(implementation);
 IDisposable disposbale = service as IDisposable;
 if (null != disposbale)
 {
  _disposables.Add(disposbale);
 }
 return service;
 }
 return _innerServiceProvider.GetService(serviceType);
 }
 
 public void Dispose()
 {
 (_innerServiceProvider as IDisposable)?.Dispose();
 foreach (var it in _disposables)
 {
 it.Dispose();
 }
 _disposables.Clear();
 }
 }

ASP.NET Core アプリケーションでは次のように MyServiceProvider を使用します。次のコード スニペットのように、登録された Starup タイプで、ConfigureServices メソッドが MyServiceProvider オブジェクトを返すようにします。サービス インターフェイス IFoobar と実装タイプ Foobar の間のマッピングは、この MyServiceProvider オブジェクトに登録されます。リクエストを処理するとき、現在の HttpContext オブジェクトの RequestServices 属性を使用してリクエスト処理用のサービスを提供する ServiceProvider を取得し、それを使用して登録されている IFoobar サービスを取得しようとします。


 public class Program
 {
 public static void Main(string[] args)
 {
 new WebHostBuilder()
 .UseKestrel()
 .UseStartup<Startup>()
 .Build()
 .Run();
 }
 }
 
 public class Startup
 {
 public IServiceProvider ConfigureServices(IServiceCollection services)
 {
 return new MyServiceProvider(services.BuildServiceProvider())
 .Register<IFoobar, Foobar>();
 }
 
 public void Configure(IApplicationBuilder app)
 {
 app.UseDeveloperExceptionPage()
 .Run(async context => await context.Response.WriteAsync(context.RequestServices.GetRequiredService<IFoobar>().GetType().Name));
 }
 }
 public interface IFoobar { }
 public class Foobar : IFoobar { }

アプリケーション全体は非常にシンプルで問題はなさそうですが、アプリケーションを起動し、ブラウザを使用してアプリケーションにアクセスすると、次のエラーが表示されます。エラー メッセージは、サービス インターフェイス IFoobar が登録されていないことを示します。

2. 理由は何ですか?

返された ServiceProvider に IFoobar と Foobar の間のマッピング関係が明確に登録されているのはなぜですか?唯一の説明は、ConfigureServices メソッドによって返される ServiceProvider と HttpContext の RequestServices によって返される ServiceProvider がまったく同じではないということです。実際、それらは同じオブジェクトではありません。

ConfigureServices メソッドによって返された ServiceProvider は、WebHost の ServiceProvider として使用されます。WebHost は、この ServiceProvider に基づいて HttpContext の RequestServices 属性として新しい ServiceProvider を作成します。これら 2 つの ServiceProvider は親子管理されます。いつものように、RequestServices によって返される ServiceProvider が ConfigureServices メソッドによって返される ServiceProvider に基づいて作成されている場合、登録されたサービス タイプ IFoobar も識別できるはずですが、それでもエラーが発生するのはなぜでしょうか。

この問題を理解するには、このいわゆる「子 ServiceProvider」がどのように作成されるかを知る必要があります。これには、ServiceScope の概念が関係します。簡単に言えば、ServiceScope は ServiceProvider のカプセル化であり、前者は後者のライフサイクルを決定します。 ServiceScope は ServiceScopeFactory によって作成され、「親 ServiceProvider」にサービスとして登録されます。 「親 ServiceProvider」が「子 ServiceProvider」を作成する必要がある場合、GetService メソッドを呼び出して ServiceScopeFactory オブジェクト (使用されるサービス インターフェイスは IServiceScopeFactory) を取得し、後者を使用してこの ServiceScope によって提供される ServiceProvider を作成します。返された「子 ServiceProvider」です。

しかし、MyServiceProvider オブジェクトの場合、その GetService メソッドが呼び出されて ServiceScopeFactory オブジェクトを取得しようとすると、実際に取得されるのは、カプセル化された SerivceProvider に関連付けられた ServiceScopeFactory なので、自然に作成される「子 ServiceProvider」も MyServiceProvider に関連します。関係ないよ。

3. この問題を解決するにはどうすればよいですか?

問題の根本がわかったので、当然解決策も見つかります。解決策は複雑ではありません。MyServiceProvider の GetService メソッドを使用して、独自のサービス登録を反映する ServiceScopeFactory を返すだけです。この目的のために、次の ServiceScope と対応する ServiceScopeFactory を定義します。


 internal class ServiceScope : IServiceScope
 {
 private MyServiceProvider _serviceProvider;
 
 public ServiceScope(IServiceScope innserServiceScope, Dictionary<Type, Type> services)
 {
 _serviceProvider = new MyServiceProvider(innserServiceScope.ServiceProvider, services);
 }
 public IServiceProvider ServiceProvider
 {
 get { return _serviceProvider; }
 }
 
 public void Dispose()
 {
 _serviceProvider.Dispose();
 }
 }
 
 internal class ServiceScopeFactory : IServiceScopeFactory
 {
 private IServiceScopeFactory _innerServiceFactory;
 private Dictionary<Type, Type> _services;
 
 public ServiceScopeFactory(IServiceScopeFactory innerServiceFactory, Dictionary<Type, Type> services)
 {
 _innerServiceFactory = innerServiceFactory;
 _services = services;
 }
 public IServiceScope CreateScope()
 {
 return new ServiceScope(_innerServiceFactory.CreateScope(), _services);
 }
 }

さらに、MyServiceProvider のコンストラクターを追加し、GetService メソッドにも IServiceScopeFactory の対応するコードを追加しました。


 public class MyServiceProvider : IServiceProvider, IDisposable
{
 public MyServiceProvider(IServiceProvider innerServiceProvider, Dictionary<Type, Type> services)
 {
 _innerServiceProvider = innerServiceProvider;
 _services = services;
 _disposables = new List<IDisposable>();
 }
 
 public object GetService(Type serviceType)
 {
 if (serviceType == typeof(IServiceScopeFactory))
 {
 IServiceScopeFactory innerServiceScopeFactory = _innerServiceProvider.GetRequiredService<IServiceScopeFactory>();
 return new ServiceScopeFactory(innerServiceScopeFactory, _services);
 }
 ... 
 }
 ...
 }

上記の共有が、そのような問題を解決する必要がある友人に役立つことを願っています!

以上がASP.NET Core アプリケーションでのサードパーティ IoC/DI フレームワークとの統合の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。