Maison  >  Article  >  développement back-end  >  Intégration avec des frameworks IoC/DI tiers dans les applications ASP.NET Core

Intégration avec des frameworks IoC/DI tiers dans les applications ASP.NET Core

巴扎黑
巴扎黑original
2017-04-17 10:44:521642parcourir

Dans les applications ASP.NET Core, l'intégration de frameworks DI tiers peut être réalisée en renvoyant un ServiceProvider dans la méthode ConfigureServices qui définit le type de démarrage. Mais ce n'est pas si simple. Laissez-moi le partager avec vous à travers un exemple

1 Le ServiceProvider renvoyé par la méthode ConfigureServices est inutile !

Nous pouvons utiliser un. exemple simple pour expliquer ce problème. Nous avons d'abord défini le MyServiceProvider suivant, qui est en fait une encapsulation d'un autre ServiceProvider. Pour plus de simplicité, nous utilisons un dictionnaire pour enregistrer la relation de mappage entre l'interface de service et le type d'implémentation. Cette relation peut être enregistrée en appelant la méthode Register. Dans la méthode GetService qui fournit une instance de service, si le type de service fourni a été enregistré, nous créerons et renverrons l'objet instance correspondant, sinon nous utiliserons le ServiceProvider encapsulé pour fournir des services. Afin de garantir que l'instance de service puisse être recyclée normalement, si le type de service implémente l'interface IDisposable, nous l'ajouterons à la collection représentée par le champ _disposables. Lorsque la méthode Dispose de MyServiceProvider est appelée, les méthodes Dispose de ces instances de service fournies seront appelées.


 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();
 }
 }

Nous utilisons MyServiceProvider dans une application ASP.NET Core comme suit. Comme dans l'extrait de code suivant, dans le type Starup enregistré, nous laissons la méthode ConfigureServices renvoyer un objet MyServiceProvider. Le mappage entre l'interface de service IFoobar et le type d'implémentation Foobar est enregistré sur cet objet MyServiceProvider. Lors du traitement de la demande, nous utilisons l'attribut RequestServices de l'objet HttpContext actuel pour obtenir le ServiceProvider qui fournit des services pour le traitement des demandes, et essayons de l'utiliser pour obtenir le service IFoobar enregistré.


 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 { }

L'ensemble de l'application est si simple, et il ne semble y avoir aucun problème, mais lorsque nous démarrons l'application et utilisons le navigateur pour accéder à l'application, ce qui suit une erreur apparaîtra. Le message d'erreur indique que l'interface de service IFoobar n'a pas été enregistrée.

2. Quelle est la raison ?

Nous avons clairement enregistré la relation de mappage entre IFoobar et Foobar dans le ServiceProvider renvoyé. Pourquoi le ServiceProvider renvoyé par RequestServices indique-t-il que le service n'a pas encore été enregistré ? La seule explication est que le ServiceProvider renvoyé par la méthode ConfigureServices et le ServiceProvider renvoyé par RequestServices de HttpContext ne sont pas du tout identiques. En fait, ce ne sont pas les mêmes objets.

Le ServiceProvider renvoyé par la méthode ConfigureServices sera utilisé comme ServiceProvider de WebHost Pour chaque requête reçue, WebHost créera un nouveau ServiceProvider basé sur ce ServiceProvider en tant qu'attribut RequestServices de HttpContext. gestion des enfants. Comme d'habitude, si le ServiceProvider renvoyé par RequestServices est créé sur la base du ServiceProvider renvoyé par la méthode ConfigureServices, il devrait également être capable d'identifier le type de service enregistré IFoobar, alors pourquoi l'erreur se produit-elle toujours ?

Pour comprendre ce problème, vous devez savoir comment ce soi-disant « ServiceProvider enfant » est créé, ce qui implique le concept de ServiceScope. En termes simples, ServiceScope est une encapsulation d'un ServiceProvider, et le premier détermine le cycle de vie du second. ServiceScope est créé par ServiceScopeFactory, qui est enregistré en tant que service auprès du « ServiceProvider parent ». Lorsque le "ServiceProvider parent" doit créer un "ServiceProvider enfant", il appellera la méthode GetService pour obtenir l'objet ServiceScopeFactory (l'interface de service utilisée est IServiceScopeFactory), et utilisera ce dernier pour créer un ServiceScope fourni par ce ServiceScope. est le "ServiceProvider enfant" renvoyé.

Mais pour notre objet MyServiceProvider, lorsque sa méthode GetService est appelée pour tenter d'obtenir l'objet ServiceScopeFactory, ce qui est obtenu est en fait le ServiceScopeFactory associé au SerivceProvider encapsulé, il est donc naturel de créer un "ServiceProvider enfant" » n'a également rien à voir avec MyServiceProvider.

3. Comment résoudre ce problème ?

Maintenant que nous connaissons la racine du problème, nous avons une solution. La solution n'est pas compliquée, nous avons seulement besoin de la méthode GetService de MyServiceProvider pour renvoyer le ServiceScopeFactory qui reflète son propre enregistrement de service. À cette fin, nous définissons le ServiceScope suivant et le ServiceScopeFactory correspondant.


 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);
 }
 }

De plus, nous avons ajouté un constructeur pour MyServiceProvider, et la méthode GetService a également ajouté le code correspondant pour 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);
 }
 ... 
 }
 ...
 }

Le partage ci-dessus, j'espère qu'il sera utile aux amis qui ont besoin de résoudre de tels problèmes !

Ce qui précède est le contenu détaillé de. pour plus d'informations, suivez d'autres articles connexes sur le site Web de PHP en chinois!

Déclaration:
Le contenu de cet article est volontairement contribué par les internautes et les droits d'auteur appartiennent à l'auteur original. Ce site n'assume aucune responsabilité légale correspondante. Si vous trouvez un contenu suspecté de plagiat ou de contrefaçon, veuillez contacter admin@php.cn