Eu gostaria de ter certeza de que entendi o conceito de injeção de dependência (DI). Bem, eu realmente entendo o conceito, DI não é complicado: você cria uma interface e passa a implementação da minha interface para a classe que a usa. A maneira comum de transmiti-lo é por construtor, mas você também pode transmiti-lo por setter ou outro método.
O que não tenho certeza de entender é quando usar o DI.
Uso 1: É claro que usar DI, caso você tenha várias implementações de sua interface, parece lógico. Você tem um repositório para o SQL Server e outro para o banco de dados Oracle. Ambos compartilham a mesma interface e você "injeta" (este é o termo usado) o que você deseja no tempo de execução. Isso nem é DI, é a programação básica do OO aqui.
Uso 2: Quando você tem uma camada de negócios com muitos serviços com todos os métodos específicos, parece que a boa prática é criar uma interface para cada serviço e também injetar a implementação, mesmo que essa seja única. Porque isso é melhor para manutenção. Esse é o segundo uso que não entendo.
Eu tenho algo como 50 classes de negócios. Nada é comum entre eles. Alguns são repositórios que obtêm ou salvam dados em 3 bancos de dados diferentes. Alguns leem ou escrevem arquivos. Alguns fazem pura ação comercial. Existem também validadores e auxiliares específicos. O desafio é o gerenciamento de memória, porque algumas classes são instanciadas a partir de locais diferentes. Um validador pode chamar vários repositórios e outros validadores que podem chamar os mesmos repositórios novamente.
Exemplo: camada de negócios
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 exemplo é muito básico, mas você vê como eu posso criar facilmente duas instâncias de itemService para obter um itemSite. Além disso, cada serviço também vem com seu contexto de banco de dados. Portanto, esta chamada criará 3 DbContext. 3 Conexões.
Minha primeira idéia foi criar um singleton para reescrever todo esse código como abaixo. O código é mais legível e mais importante: o sistema singleton cria apenas uma instância de cada serviço usado e cria na primeira chamada. Perfeito, exceto que ainda tenho contextos diferentes, mas posso fazer o mesmo sistema para meus contextos. Tão feito.
Camada de negócios
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);
}
}
Algumas pessoas me dizem, de acordo com as boas práticas, que eu deveria usar o DI com instância única e usar o singleton é uma prática ruim. Eu devo criar uma interface para cada classe de negócios e instancia-la com a ajuda do contêiner de DI. Mesmo? Este DI simplifica meu código. Difícil de acreditar.