Comment configurer un conteneur d’injecteur simple et un style de vie dans une application Web MVC avec WebAPI, WCF, SignalR et Background Task

La documentation sur l’injecteur simple fournit d’excellents exemples sur la configuration du conteneur pour WebRequest, Web API, WCF, … mais les exemples sont spécifiques à une technologie / à un mode de vie à la fois. Notre application Web utilise la plupart d’entre eux ensemble! Je ne sais pas comment configurer le conteneur pour qu’il fonctionne avec plusieurs styles de vie.


Disons que j’ai un projet MVC avec une API Web. J’ai les objects suivants:

  • MyDbContext: Mon code d’entité premier contexte de firebase database
  • IMyDataProvider implémenté par MyDataProvider: contient la logique de requête et utilise MyDbContext
  • MyController: contrôleur MVC utilisant IMyDataProvider
  • MyApiController: contrôleur WebApi utilisant IMyDataProvider

Devrais-je créer et configurer un conteneur pour chaque type de style de vie?

Lorsque je tout enregistre avec RegisterPerWebRequest fonctionne dans les deux types de contrôleurs. Est-ce sécuritaire? Ou vais-je rencontrer des problèmes lors de l’utilisation de async / wait dans un contrôleur API Web?

Quelle est la meilleure configuration lorsque des contrôleurs MVC et Web API sont injectés dans les mêmes instances?

Devrais-je utiliser un mode de vie hybride?


Maintenant, pour compliquer les choses … notre application utilise également des tâches en arrière-plan et SignalR.

Ces deux événements surviennent parfois en dehors d’une requête Web et nécessitent l’access aux mêmes objects que ceux décrits ci-dessus.

La meilleure solution serait d’utiliser une scope à vie?

Aurais-je besoin de créer un nouveau conteneur pour ce style de vie? ou puis-je réutiliser / reconfigurer mon conteneur MVC / Web API?

Y a-t-il un sortingple style de vie?

Généralement, vous n’avez pas besoin d’un conteneur par style de vie. En général, vous voulez avoir une instance de conteneur par AppDomain. Cependant, mélanger l’API Web dans le même projet avec MVC est, d’un sharepoint vue architectural, une horrible idée pour l’OMI (comme expliqué ici , ici et ici ). Donc, si vous séparez ces pièces dans leurs propres blocs architecturaux, vous aurez déjà moins de problèmes.

Toutefois, si vous utilisez MVC et l’API Web dans le même projet, cela signifie que vous utiliserez toujours l’API Web. WebApiRequestLifestyle a été explicitement conçu pour fonctionner:

ainsi à l’intérieur et à l’extérieur d’IIS. C’est-à-dire qu’il peut fonctionner dans un projet d’API Web auto-hébergé où il n’y a pas de HttpContext.Current. ( source )

En règle générale, il est prudent d’utiliser WebRequestLifestyle si vous ne vous exécutez dans IIS que si vous n’avez pas l’intention de faire tourner des opérations parallèles à l’aide de ConfigureAwait(false) (ce qui devrait être vraiment rare), comme expliqué ici .

Ainsi, dans le cas où vous mélangez encore l’API Web avec MVC dans le même projet, il n’y a aucune raison d’utiliser un mode de vie hybride ; vous pouvez simplement utiliser le même style de vie. Pour effectuer le traitement en arrière-plan, vous devrez peut-être créer un style de vie hybride, mais chaque scénario nécessite un hybride différent. Cependant, les hybrides peuvent être empilés et vous pouvez facilement créer un «sortingple style de vie» si nécessaire.

Étant donné que vous souhaitez effectuer un traitement en arrière-plan avec SignalR, vous devez décider du type de mode de vie défini pour l’exécution de ces opérations en arrière-plan. Le style de vie le plus évident est le LifetimeScopeLifestyle, ce qui signifie que vous devez enregistrer vos enregistrements de scope avec le style de vie suivant:

 var hybridLifestyle = Lifestyle.CreateHybrid( lifestyleSelector: () => HttpContext.Current != null, trueLifestyle: new WebRequestLifestyle(), falseLifestyle: new LifetimeScopeLifestyle()); 

Cependant, une étendue de durée de vie doit être démarrée explicitement (comme l’étendue de la requête Web a été démarrée implicitement pour vous si vous incluez SimpleInjector.Integration.Web.dll dans votre application Web). La procédure à suivre dépend de votre conception, mais cette question de SignalR peut vous orienter dans la bonne direction.

Je dois dire que je suis tombé sur un scénario similaire il y a quelque temps. J’ai fini par partager ma configuration via mon API Web et signalR, mais vous devez implémenter un mode de vie personnalisé pour signalR car il n’est pas basé sur une requête Web.

spécialement dans signalR, vous trouverez quelques problèmes de gestion des dépendances par requête Web dans un concentrateur. Certains d’entre eux seront nuls, comme httpContext.Current parmi d’autres.

La solution:

Vous avez besoin d’un mode de vie hybride entre WebRequestLifestlye et Lifestyle.Transient, Lifestyle.Singleton ou LifetimeScopeLifestyle. J’ai fini d’utiliser le motif de décorateur, vous pouvez lire cet article et cet autre article .

mon décorateur

 public class CommandLifetimeScopeDecorator : ICommandHandler { private readonly Func> _handlerFactory; private readonly Container _container; public CommandLifetimeScopeDecorator( Func> handlerFactory, Container container) { _handlerFactory = handlerFactory; _container = container; } public void Handle(T command) { using (_container.BeginLifetimeScope()) { var handler = _handlerFactory(); // resolve scoped dependencies handler.Handle(command); } } } public interface ICommandHandler { void Handle(T command); } 

J’ai géré les dépendances à l’aide d’un activateur de concentrateur pour signalR

 public class MyHubActivator : IHubActivator { private readonly Container _container; public MyHubActivator(Container container) { _container = container; } public IHub Create(HubDescriptor descriptor) { return _container.GetInstance(descriptor.HubType) as IHub; } } 

un fichier racine composite qui est l’endroit où vous allez gérer vos dépendances

 public CompositRoot(Container container) { _container = container; } public container Configure() { // _container.Registerall container dependencies return _container; } 

puis partagez votre configuration racine composite lorsque vous démarrez votre application

 var compositRoot = new CompositRoot(simpleInjector.Container); //simple injector instance compositRoot.Configure(); 

Pour signalR

 GlobalHost.DependencyResolver.Register(typeof(IHubActivator), () => new MyHubActivator(compositRoot)); 

et vous pouvez réutiliser votre configuration entre d’autres projets!

mes deux sous espèrent que ça aide!