È una buona idea ritardare qualsiasi decisione di utilizzare qualche tipo di strumento o libreria fino al the last responsible moment. Con un buon design è possibile aggiungere una libreria DI in seguito. Ciò significa che ti alleni con lo Pure DI.
Il punto di intercettazione preferito in MVC è l'astrazione IControllerFactory
poiché consente di intercettare la creazione di controller MVC, impedendo così di dover implementare un secondo costruttore (che è is an anti-pattern). Anche se è possibile utilizzare IDependencyResolver
, l'uso di tale astrazione è molto meno conveniente perché è anche chiamato da MVC per risolvere le cose che non sono in genere interessati a.
Una consuetudine IControllerFactory
che fungerà da vostro composition root possono essere implementate come segue:
public sealed class CompositionRoot : DefaultControllerFactory
{
private static string connectionString =
ConfigurationManager.ConnectionStrings["app"].ConnectionString;
private static Func<BooksContext> bookContextProvider = GetCurrentBooksContext;
private static IBookRepository bookRepo = new BookRepository(bookContextProvider);
private static IOrderBookHandler orderBookHandler = new OrderBookHandler(bookRepo);
protected override IController GetControllerInstance(RequestContext _, Type type) {
// Unfortunately, because of MVC's design, controllers are not stateless, and
// you will have to create them per request.
if (type == typeof(OrderBookController))
return new HomeController(orderBookHandler);
if (type == typeof(BooksController))
return new BooksController(bookRepo);
// [other controllers here]
return base.GetControllerInstance(_, type);
}
private static BooksContext GetCurrentBooksContext() {
return GetRequestItem<BooksContext>(() => new BooksContext(connectionString));
}
private static T GetRequestItem<T>(Func<T> valueFactory) where T : class {
var context = HttpContext.Current;
if (context == null) throw new InvalidOperationException("No web request.");
var val = (T)context.Items[typeof(T).Name];
if (val == null) context.Items[typeof(T).Name] = val = valueFactory();
return val;
}
}
Il nuovo controller fabbrica può essere agganciato in MVC come segue:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start() {
ControllerBuilder.Current.SetControllerFactory(new CompositionRoot());
// the usual stuff here
}
}
Quando si pratica pura dI, in genere vedere il tuo composit La radice ionica consiste in una lunga lista di dichiarazioni if
. Un'istruzione per oggetto root nell'applicazione.
L'avvio con Pure DI presenta alcuni vantaggi interessanti. Il più importante è il supporto in fase di compilazione, perché è qualcosa che perderai immediatamente quando inizi a utilizzare una libreria DI. Alcune librerie cercano di minimizzare questa perdita consentendo di verificare la configurazione in un modo che il compilatore farebbe; ma questa verifica viene eseguita in fase di esecuzione e il ciclo di feedback non è mai così breve come quello che il compilatore può darti.
Si prega di non essere tentati di semplificare lo sviluppo implementando un meccanismo che consente di creare tipi utilizzando la riflessione, perché in tal modo si sta costruendo la propria libreria DI. Ci sono molti aspetti negativi a questo, ad es. si perde il supporto per la compilazione del tempo senza recuperare i benefici che una libreria DI esistente può darvi.
Quando la radice di composizione sta diventando difficile da mantenere, è il momento in cui dovresti prendere in considerazione il passaggio da DI puro a una libreria DI.
Do notare che nella mia radice composizione di esempio, tutti componenti applicative (ad eccezione per i controllori) sono definiti come Singleton. Singleton significa che l'applicazione avrà solo un'istanza di ciascun componente. Questo progetto richiede che i componenti siano privi di stato (e quindi sicuri per i thread), qualsiasi cosa abbia lo stato (ad esempio BooksContext
) should not be injected through the constructor. Nell'esempio ho utilizzato uno Func<T>
come provider dello BooksContext
che viene memorizzato per richiesta.
Rendere i vostri singleton grafici di oggetti ha molti vantaggi interessanti. Ad esempio, ti impedisce di fare errori di configurazione comuni come Captive Dependencies e ti costringe a un design più SOLIDO. Inoltre, alcune librerie DI sono molto lente e rendere tutto singleton può impedire problemi di prestazioni quando si passa a una libreria DI in seguito. D'altro canto, il lato negativo di questo design è che tutti i membri del team dovrebbero capire che tutti i componenti devono essere apolidi. Lo stato di conservazione nei componenti causerà dolore e aggravamento inutili. La mia esperienza è che i componenti stateful sono molto più facili da rilevare rispetto alla maggior parte degli errori di configurazione DI.Ho anche notato che avere componenti singleton è qualcosa che sembra naturale per molti sviluppatori, specialmente per quelli che non hanno esperienza con DI.
Si noti che nell'esempio ho implementato manualmente uno stile di vita per richiesta per BooksContext
. Sebbene tutte le librerie DI abbiano un supporto immediato per gli stili di vita con scope come gli stili di vita per richiesta, non farei uso di quegli stili di vita con ambito (tranne forse quando la libreria garantisce di lanciare un'eccezione invece di fallire silenziosamente). La maggior parte delle librerie non ti avvisa quando risolvi un'istanza di ambito al di fuori del contesto di un ambito attivo (ad esempio risolvendo un'istanza per richiesta su un thread in background). Alcuni contenitori ti restituiscono un'istanza singleton, altri ti restituiscono una nuova istanza ogni volta che chiedi. Questo è davvero fastidioso perché nasconde bug e può causare molte ore nel tentativo di eseguire il debug dell'applicazione (parlo per esperienza qui).
Consiglierei SimpleInjector in quanto convalida la configurazione prima dell'avvio. Pochissimi contenitori lo fanno. Quindi qualsiasi problema in genere dovrebbe essere trovato e risolto all'inizio. – jgauffin
Se a un certo punto si sta pianificando l'upgrade del progetto a ASP.NET vNext, vale la pena menzionare che includerà alcune implementazioni di base del contenitore DI che possono soddisfare le proprie esigenze (http://blogs.msdn.com/b /webdev/archive/2014/06/17/dependency-injection-in-asp-net-vnext.aspx) –
Penso che sarebbe molto meglio iniziare con DI puro invece di utilizzare DI di ASP.NET vNext integrato biblioteca. [La libreria DI incorporata è inutile quando si scrivono applicazioni SOLID] (https://stackoverflow.com/a/30682214/264697). – Steven