2014-10-17 4 views
11

La semplice documentazione dell'iniettore fornisce ottimi esempi su come configurare il contenitore per WebRequest, Web API, WCF, ... ma gli esempi sono specifici per una tecnologia/stile di vita alla volta. La nostra applicazione web usa la maggior parte di loro insieme! Non mi è chiaro come configurare il contenitore per lavorare con diversi stili di vita.Come configurare un semplice contenitore di iniettori e lifestylse in un'app Web MVC con WebAPI, WCF, SignalR e attività in background


Diciamo che ho un progetto MVC con Web API. Ho i seguenti oggetti:

  • MyDbContext: Il mio codice entità primo contesto db
  • IMyDataProvider attuato da MyDataProvider: Contiene logica di query e usa MyDbContext
  • MyController: Controller MVC che utilizza IMyDataProvider
  • MyApiController: WebAPI controller che utilizza IMyDataProvider

Devo creare e configurare un contenitore per ogni tipo di stile di vita?

Quando registro tutto con RegisterPerWebRequest<T> funziona con entrambi i tipi di controller. È sicuro? O avrò problemi quando utilizzo async/await in un controller API Web?

Qual è la migliore configurazione quando ho entrambi i controller MVC e Web API che vengono iniettati le stesse istanze?

Devo utilizzare uno stile di vita ibrido?


Ora per complicare le cose ... la nostra applicazione utilizza anche attività in background e SignalR.

Entrambe si verificano talvolta al di fuori di una richiesta Web e richiedono l'accesso agli stessi oggetti descritti in precedenza.

La soluzione migliore sarebbe utilizzare un ambito Lifetime?

Devo creare un nuovo contenitore per questo stile di vita? o posso riutilizzare/riconfigurare il mio contenitore MVC/Web API?

C'è un triplo stile di vita?

risposta

4

Di solito non è necessario avere un contenitore per stile di vita; In generale, si desidera avere un'istanza contenitore per AppDomain. Tuttavia, la combinazione di API Web nello stesso progetto con MVC è da un punto di vista architettonico un'idea IMO orribile (come spiegato here, here e here). Quindi, nel caso tu stia separando quelle parti nei loro blocchi architettonici, avrai già meno problemi.

Tuttavia, nel caso in cui si stiano eseguendo MVC e Web API nello stesso progetto, ciò significa fondamentalmente che si utilizzerà sempre l'API Web. Il WebApiRequestLifestyle è stato creato esplicitamente per funzionare:

bene sia all'interno che all'esterno di IIS. Ad esempio, può funzionare in un progetto API Web self-hosted in cui non è presente HttpContext.Current. (source)

In generale, è sicuro da usare il WebRequestLifestyle nel caso in cui si esegue solo in IIS quando si ha alcuna intenzione di girare di operazioni parallele utilizzando ConfigureAwait(false) (che dovrebbe essere davvero raro IMO), come spiegato here.

Quindi, nel caso in cui si stia ancora mescolando API Web con MVC nello stesso progetto, non c'è motivo di utilizzare un hybrid lifestyle; puoi semplicemente usare lo stesso stile di vita. Per eseguire l'elaborazione in background, potrebbe tuttavia essere necessario creare uno stile di vita ibrido, ma ogni scenario richiede un ibrido diverso. Tuttavia, gli ibridi possono essere impilati e, se necessario, è possibile creare facilmente un "triplo stile di vita".

Poiché si desidera eseguire l'elaborazione in background con SignalR, è necessario decidere in quale tipo di scoped lifestyle eseguire tali operazioni in background. Lo stile di vita più evidente è il LifetimeScopeLifestyle e questo significa che si dovrebbe fare le registrazioni con ambito utilizzando il seguente stile di vita con ambito:

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

Un ambito di vita deve tuttavia essere avviato in modo esplicito (come fosse la portata richiesta Web viene avviato in modo implicito per voi se includi SimpleInjector.Integration.Web.dll nella tua applicazione web). Come farlo dipende dal tuo progetto, ma this q/a about SignalR potrebbe indirizzarti nella giusta direzione.

+0

Sapevo che MVC e WebAPI non erano la stessa cosa, ma devo dire che sono sorpreso di sentire che è un cattivo progetto mescolare entrambi nello stesso progetto. Considerando che saranno uniti in MVC6, sono in grado di vivere con questa architettura. L'uso di uno stile di vita ibrido sembra essere il modo più semplice per utilizzare Simplje Injector con MVC, WebAPI e SignalR/BgTasks. Tuttavia avevo bisogno di cambiare _lifestyleSelector_ come segue: 'container.GetCurrentLifetimeScope() == null && HttpContext.Current! = Null' per utilizzare esplicitamente l'ambito quando esiste anche se HttpContext potrebbe esistere (all'interno della richiesta SignalR) – Chris

7

Devo dire, mi sono imbattuto in uno scenario simile qualche tempo fa, ho finito per condividere la mia configurazione sulla mia web API e signalR, ma è necessario implementare uno stile di vita personalizzato per signalR poiché non è basato sulla richiesta web .

specialmente in signalR troverete alcuni problemi di gestione delle dipendenze per-web-request in un hub alcuni di questi saranno nulli come httpContext.Current tra gli altri.

La soluzione:

È necessario uno stile di vita ibrido tra WebRequestLifestlye eo Lifestyle.Transient, Lifestyle.Singleton o LifetimeScopeLifestyle. Alla fine ho finito di usare il modello decoratore, puoi leggere questo post e questo altro post.

mia decoratrice

public class CommandLifetimeScopeDecorator<T> : ICommandHandler<T> 
    { 
     private readonly Func<ICommandHandler<T>> _handlerFactory; 
     private readonly Container _container; 

     public CommandLifetimeScopeDecorator(
     Func<ICommandHandler<T>> 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<in T> 
    { 
     void Handle(T command); 
    } 

sono riuscito le dipendenze usando un attivatore hub per 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 root file composito che è dove si sta andando a gestire le dipendenze

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

quindi condividi la configurazione radice composita quando esegui il bootstrap della tua app

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

Per signalR

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

e si può riutilizzare la configurazione tra gli altri progetti!

i miei due centesimi speranza che aiuta!

+0

Grazie, la risposta è corretta ma richiede un cambiamento architettonico più grande. – Chris

+0

nessun problema! Saluti! –

+0

Jack, sto avendo lo stesso problema, hai questa soluzione su un git per condividere? Grazie – rsegovia