Ok, ecco come ho capito che IoC e DI in Web API funzionano quando uso Castle Windsor.
Nota, però, che la mia fiducia nel fatto che io la capisca come dovrei, comunque, cade da qualche parte tra la mia sicurezza che potrei meglio Dennis Rodman in una partita di basket uno contro uno e la mia fiducia nella sua sanità mentale.
Un altro modo per dirlo è che ho passato gli ultimi giorni a leggere su IoC / DI / Castle Windsor con Web API e a sperimentarlo, ma mi sento ancora stanco di rispondere, "Tutto tranne nove, pard; sistemali nell'altro vicolo. "
Ho diverse domande più specifiche su Stack Overflow, ad esempio questo , ma non sono ancora molto più chiaro a riguardo.
Quindi ho intenzione di "parlare ad alta voce" e spero che qualcuno risponda con qualcosa che raffini la mia comprensione, faccia luce su questo argomento opaco (per me), o aiuti a svelare questo nodo gordiano.
Punto: La base di IoC / DI consiste nel passare le interfacce ai costruttori, in modo che la classe con il costruttore non debba istanziare i propri oggetti; in effetti, non ha bisogno di conoscere la componente / classe concreta.
Punto: per configurarlo, hai bisogno di un codice come questo nei tuoi Controller:
private readonly IDepartmentRepository _deptsRepository;
public DepartmentsController(IDepartmentRepository deptsRepository)
{
if (deptsRepository == null)
{
throw new ArgumentNullException("deptsRepository is null");
}
_deptsRepository = deptsRepository;
}
Punto: alcune classi che implementano IDepartmentRepository vengono passate al costruttore DepartmentsController
Punto: il codificatore non lo fa esplicitamente - il contenitore IOC (Castle Windsor, in questo caso) lo fa "automaticamente" intercettando il meccanismo di routing dell'API Web con il proprio, come con un codice come questo, nel globale. asax.cs:
GlobalConfiguration.Configuration.Services.Replace(
typeof(IHttpControllerActivator), new WindsorCompositionRoot(_container));
Point: global.asax.cs 'Application_Start () lancia la palla e chiama il programma di installazione / registrazione con codice come:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
ConfigureWindsor(GlobalConfiguration.Configuration);
}
public static void ConfigureWindsor(HttpConfiguration configuration)
{
_container = new WindsorContainer();
_container.Install(FromAssembly.This());
_container.Kernel.Resolver.AddSubResolver(new CollectionResolver(_container.Kernel, true));
var dependencyResolver = new WindsorDependencyResolver(_container);
configuration.DependencyResolver = dependencyResolver;
}
Punto: Molte altre impalcature di Castle Windsor devono essere installate per questo. Nello specifico, le classi devono essere "installate" (registrato - credo che sarebbe stato registrato come termine migliore / più facile da usare).
Questa è una delle mie principali aree "say what?" / headscratching. Non so se il codice di registrazione dovrebbe apparire così:
public object GetService(Type serviceType)
{
return _container.Kernel.HasComponent(serviceType) ? _container.Resolve(serviceType) : null;
}
public IEnumerable<object> GetServices(Type serviceType)
{
if (!_container.Kernel.HasComponent(serviceType))
{
return new object[0];
}
return _container.ResolveAll(serviceType).Cast<object>();
}
... o in questo modo:
public class ApiControllersInstaller : IWindsorInstaller
{
public void Install(Castle.Windsor.IWindsorContainer container, Castle.MicroKernel.SubSystems.Configuration.IConfigurationStore store)
{
container.Register(Classes.FromThisAssembly() // should it be Types instead of Classes?
.BasedOn<ApiController>()
.LifestylePerWebRequest());
}
}
... o in questo modo:
public class ServiceInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component.For<IDeliveryItemRepository>().ImplementedBy<DeliveryItemRepository>().LifestylePerWebRequest(),
Component.For<IDeliveryRepository>().ImplementedBy<DeliveryRepository>().LifestylePerWebRequest(),
Component.For<IDepartmentRepository>().ImplementedBy<DepartmentRepository>().LifestylePerWebRequest(),
Component.For<IExpenseRepository>().ImplementedBy<ExpenseRepository>().LifestylePerWebRequest(),
Component.For<IInventoryItemRepository>().ImplementedBy<InventoryItemRepository>().LifestylePerWebRequest(),
Component.For<IInventoryRepository>().ImplementedBy<InventoryRepository>().LifestylePerWebRequest(),
Component.For<IItemGroupRepository>().ImplementedBy<ItemGroupRepository>().LifestylePerWebRequest());
}
}
... o altrimenti ...
Point: una volta che tutto questo è stato impostato, e i Controller (e i Repository? C'è più di quello che ho su di loro, per renderli sicuri per CW?) sono stati registrati dal motore di routing Castle Windsor, una chiamata alla mia app Web API come:
http://platypus:28642/api/Departments
... verrà risolto in un "componente" (classe concreta) che implementa IDepartmentRepository. CW sarà in grado di capirlo, finché avrò una classe che implementa IDepartmentRepository, che è quella che passa silenziosamente al costruttore del Controller che accetta un argomento per una classe che implementa IDepartmentRepository.
Ma ... cosa succede se ci sono classi N che implementano IDepartmentRepository? In che modo CW sa quale passerà al costruttore del Controller? Come posso, l'umile umano in questa riunione di silicio e materiale rosa squishy, indicare quale dei N voglio che CW passi al costruttore?