Me gustaría estar seguro de entender el concepto de inyección de dependencia (DI). Bueno, realmente entiendo el concepto, DI no es complicado: creas una interfaz y luego pasas la implementación de mi interfaz a la clase que la usa. La forma común de pasarlo es por constructor, pero también puede pasarlo por setter u otro método.
Lo que no estoy seguro de entender es cuándo usar DI.
Uso 1: Por supuesto, usar DI en caso de que tenga una implementación múltiple de su interfaz parece lógica. Tiene un repositorio para su SQL Server y luego otro para su base de datos Oracle. Ambos comparten la misma interfaz y usted "inyecta" (este es el término utilizado) el que desea en tiempo de ejecución. Esto ni siquiera es DI, aquí es una programación básica de OO.
Uso 2: cuando tiene una capa empresarial con muchos servicios con todos sus métodos específicos, parece que la mejor práctica es crear una interfaz para cada servicio y también inyectar la implementación, incluso si esta es única. Porque esto es mejor para el mantenimiento. Este es este segundo uso que no entiendo.
Tengo algo así como 50 clases de negocios. Nada es común entre ellos. Algunos son repositorios que obtienen o guardan datos en 3 bases de datos diferentes. Algunos leen o escriben archivos. Algunos hacen pura acción comercial. También hay validadores y ayudantes específicos. El desafío es la administración de memoria porque algunas clases se instalan desde diferentes ubicaciones. Un validador puede llamar a varios repositorios y otros validadores que pueden llamar a los mismos repositorios nuevamente.
Ejemplo: capa empresarial
public class SiteService : Service, ICrud<Site>
{
public Site Read(Item item, Site site)
{
return beper4DbContext.Site
.AsNoTracking()
.SingleOrDefault(y => y.SiteId == site.Id && y.ItemId == item.Id)
}
public Site Read(string itemCode, string siteCode)
{
using (var itemService = new ItemService())
{
var item = itemService.Read(itemCode);
return Read(item, site);
}
}
}
public class ItemSiteService : Service, ICrud<Site>
{
public ItemSite Read(Item item, Site site)
{
return beper4DbContext.ItemSite
.AsNoTracking()
.SingleOrDefault(y => y.SiteId == site.Id && y.ItemId == item.Id)
}
public ItemSite Read(string itemCode, string siteCode)
{
using (var itemService = new ItemService())
using (var siteService = new SiteService())
{
var item = itemService.Read(itemCode);
var site = siteService.Read(itemCode, siteCode);
return Read(item, site);
}
}
}
Controlador
public class ItemSiteController : BaseController
{
[Route("api/Item/{itemCode}/ItemSite/{siteCode}")]
public IHttpActionResult Get(string itemCode, string siteCode)
{
using (var service = new ItemSiteService())
{
var itemSite = service.Read(itemCode, siteCode);
return Ok(itemSite);
}
}
}
Este ejemplo es muy básico, pero puede ver cómo puedo crear fácilmente 2 instancias de itemService para obtener un itemSite. Entonces también cada servicio viene con su contexto DB. Entonces esta llamada creará 3 DbContext. 3 conexiones.
Mi primera idea fue crear singleton para reescribir todo este código como abajo. El código es más legible y lo más importante es que el sistema singleton crea solo una instancia de cada servicio utilizado y lo crea en la primera llamada. Perfecto, excepto que todavía tengo diferentes contextos pero puedo hacer el mismo sistema para mis contextos. Hecho.
Capa empresarial
public class SiteService : Service, ICrud<Site>
{
public Site Read(Item item, Site site)
{
return beper4DbContext.Site
.AsNoTracking()
.SingleOrDefault(y => y.SiteId == site.Id && y.ItemId == item.Id)
}
public Site Read(string itemCode, string siteCode)
{
var item = ItemService.Instance.Read(itemCode);
return Read(item, site);
}
}
public class ItemSiteService : Service, ICrud<Site>
{
public ItemSite Read(Item item, Site site)
{
return beper4DbContext.ItemSite
.AsNoTracking()
.SingleOrDefault(y => y.SiteId == site.Id && y.ItemId == item.Id)
}
public ItemSite Read(string itemCode, string siteCode)
{
var item = ItemService.Instance.Read(itemCode);
var site = SiteService.Instance.Read(itemCode, siteCode);
return Read(item, site);
}
}
Controlador
public class ItemSiteController : BaseController
{
[Route("api/Item/{itemCode}/ItemSite/{siteCode}")]
public IHttpActionResult Get(string itemCode, string siteCode)
{
var itemSite = service.Instance.Read(itemCode, siteCode);
return Ok(itemSite);
}
}
Algunas personas me dicen de acuerdo con las buenas prácticas que debería usar DI con una sola instancia y usar singleton es una mala práctica. Debería crear una interfaz para cada clase de negocios y crear una instancia con la ayuda del contenedor DI. De Verdad? Este DI simplifica mi código. Difícil de creer.