A Injeção de Dependências (em inglês “Dependency Injection”, ou, simplesmente, “DI”) é uma técnica que busca diminuir o acoplamento entre diferentes partes de um software. A questão envolvendo a noção de “acoplamento” é particularmente importante dentro da área de desenvolvimento, já que este conceito indica o grau de relacionamento entre diferentes partes de um sistema.

Aplicações formadas por componentes com um alto acoplamento entre si podem acarretar dificuldades futuras de manutenção. Isto acontece porque modificações em um local específico de tais projetos implicam, quase que invariavelmente, na necessidade de alterações em outros pontos. Cenários como este tendem a se agravar, sobretudo se considerada a tendência natural de aumento da complexidade em muitos projetos com o decorrer do tempo.

Ao priorizar o uso de interfaces e empregar um mecanismo responsável pela geração em tempo de execução de instâncias baseadas nestas construções, o padrão conhecido como Injeção de Dependências procura diminuir assim a interdependência entre diferentes componentes de software. Diversos são os frameworks e soluções que oferecem este tipo de funcionalidade na plataforma .NET, sendo possível mencionar o Unity Application Block (o qual é disponibilizado pela própria Microsoft), o NInject, o Autofac, o Spring.NET, dentre outras opções.

Diferentemente de suas versões anteriores, o ASP.NET 5 conta agora com um mecanismo nativo para a injeção de dependências em soluções Web (muito embora ainda esteja prevista a possibilidade de uso de frameworks como as alternativas aqui mencionadas). O objetivo deste artigo é apresentar na prática o funcionamento deste novo recurso da plataforma ASP.NET, através de um exemplo de implementação a ser detalhado na próxima seção.

Exemplo de utilização em uma aplicação MVC 6

Buscando demonstrar o novo mecanismo injeção de injeção de dependências que compõe o ASP.NET 5 será criada uma solução que faz uso dos seguintes recursos:

  • O Microsoft Visual Studio 2015 Release Candidate como IDE de desenvolvimento;
  • O .NET Framework 4.6;
  • O framework ASP.NET 5 para a criação de uma aplicação MVC 6.

Um projeto do tipo “ASP.NET Web Aplication” será gerado, tendo por nome “TesteInjDependenciaASPNET5” (Imagem 1). Ao se confirmar este primeiro passo, selecionar na sequência em “ASP.NET 5 Preview Templates” a opção “Web Site” (Imagem 2).

injdepvs2015-01
Imagem 1: Criando uma ASP.NET Web Application no Visual Studio 2015

injdepvs2015-02
Imagem 2: Selecionando o template Web Site no Visual Studio 2015

O próximo passo agora será a implementação de duas interfaces (ITesteInjDependenciaA e ITesteInjDependenciaB), as quais serão utilizadas nos testes envolvendo a resolução de dependências. Conforme é possível observar na Listagem 1, ambos as estruturas contam com uma propriedade do tipo Guid (namespace System) chamada IdReferencia:

using System;

namespace TesteInjDependenciaVS2015
{
    public interface ITesteInjDependenciaA
    {
        Guid IdReferencia { get; }
    }

    public interface ITesteInjDependenciaB
    {

        Guid IdReferencia { get; }
    }
}

Listagem 1: Interfaces ITesteInjDependenciaA e ITesteInjDependenciaB

Já na Listagem 2 constam:

  • As classes concretas TesteInjDependenciaA e TesteInjDependenciaB, as quais correspondem às implementações das duas interfaces definidas anteriormente;
  • A classe TesteInjDependenciaC, que não deriva de nenhuma outra interface (este tipo será utilizado para demonstrar outra possibilidade de uso do mecanismo de injeção de dependências, no caso sem uma interface ou classe abstrata a ser mapeada).

Em todas estas implementações concretas foram declarados construtores, com a atribuição de valores à propriedade somente leitura IdReferencia acontecendo a partir deste local (em cada uma das classes).

using System;

namespace TesteInjDependenciaVS2015
{
    public class TesteInjDependenciaA : ITesteInjDependenciaA
    {
        public Guid IdReferencia { get; }

        public TesteInjDependenciaA()
        {
            this.IdReferencia = Guid.NewGuid();
        }
    }

    public class TesteInjDependenciaB : ITesteInjDependenciaB
    {
        public Guid IdReferencia { get; }

        public TesteInjDependenciaB()
        {
            this.IdReferencia = Guid.NewGuid();
        }
    }

    public class TesteInjDependenciaC
    {
        public Guid IdReferencia { get; }

        public TesteInjDependenciaC()
        {
            this.IdReferencia = Guid.NewGuid();
        }
    }
}

Listagem 2: Classes TesteInjDependenciaA, TesteInjDependenciaB e TesteInjDependenciaC

O mapeamento das dependências a resolver em uma aplicação MVC 6 ocorrerá no método ConfigureSevices da classe Startup (um equivalente ao antigo Global.asax). Na Listagem 3 encontram-se os ajustes a serem realizados nesta estrutura:

  • A operação ConfigureSevices recebe como parâmetro uma referência do tipo IServiceCollection (namespace Microsoft.Framework.DependencyInjection), a qual será utilizada na especificação das diferentes dependências do projeto;
  • No caso da interface ITesteInjDependenciaA, o uso do método AddSingleton fará com que uma única instância de TesteInjDependenciaA seja utilizada na resolução das dependências para com ITesteInjDependenciaA encontradas ao longo de todo o ciclo de vida da solução (representando assim um exemplo prático de aplicação do pattern Singleton);
  • Já para a interface ITesteInjDependenciaB será acionado o método AddTransient. Esta instrução fará sempre com que uma nova instância de TesteInjDependenciaB seja gerada (diferentemente do caso anterior, em que uma única referência do tipo TesteInjDependenciaA serviria a qualquer instante toda a aplicação);
  • O mesmo comportamento de ITesteInjDependenciaB valerá para dependências da classe TesteInjDependenciaC (embora não se trate de uma prática muito usual, é possível também a resolução direta de uma implementação concreta).
using Microsoft.AspNet.Builder;
using Microsoft.AspNet.Diagnostics;
using Microsoft.AspNet.Hosting;
using Microsoft.Framework.ConfigurationModel;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Logging;

namespace TesteInjDependenciaVS2015
{
    public class Startup
    {
        ...

        public void ConfigureServices(IServiceCollection services)
        {
            ...

            services.AddSingleton<ITesteInjDependenciaA, TesteInjDependenciaA>();
            services.AddTransient<ITesteInjDependenciaB, TesteInjDependenciaB>();
            services.AddTransient<TesteInjDependenciaC>();
        }

        ...
    }
}

Listagem 3: Ajustes na classe Startup

A classe HomeController também precisará ser ajustada, sendo que na Listagem 4 está o código esperado para essa construção:

  • Este Controller conta com uma propriedade chamada “ObjetoA”, à qual será atribuída uma instância do tipo ITesteInjDependenciaA via mecanismo de injeção de dependências. Para que isto aconteça esta propriedade foi marcada com o atributo FromServicesAttribute (namespace Microsoft.AspNet.Mvc);
  • Também foi declarado o atributo privado “_objetoB”, baseado na interface ITesteInjDependenciaB. Um construtor também deverá ser implementado para o tipo HomeController. Será através dessa estrutura que o ASP.NET 5 irá resolver a dependência para com ITesteInjDependenciaB (o construtor de HomeController será invocado automaticamente pelo container DI);
  • A Action Index fará uso das instâncias dos tipos ITesteInjDependenciaA e ITesteInjDependenciaB, de maneira a associar tais referências a propriedades do objeto ViewBag.
using Microsoft.AspNet.Mvc;

namespace TesteInjDependenciaVS2015.Controllers
{
    public class HomeController : Controller
    {
        [FromServices]
        public ITesteInjDependenciaA ObjetoA { get; set; }

        private ITesteInjDependenciaB _objetoB;

        public HomeController(ITesteInjDependenciaB objetoB)
        {
            this._objetoB = objetoB;
        }

        public IActionResult Index()
        {
            ViewBag.ObjetoA = this.ObjetoA;
            ViewBag.ObjetoB = this._objetoB;

            return View();
        }

        ...
    }
}

Listagem 4: Ajustes na classe HomeController

OBSERVAÇÕES:

  • Por convenção, nomes de classes que representem atributos terminam com o sufixo Attribute. A utilização de expressões que envolvam um atributo vinculando o mesmo a uma estrutura de código dispensa o uso de tal sufixo ao final do nome. Logo, ao se empregar o atributo FromServicesAttribute, a propriedade marcada com essa construção estará associada apenas a uma instrução com o valor “FromServices” (entre colchetes);
  • Em versões anteriores do ASP.NET 5 e do Visual Studio 2015 a injeção de dependências em propriedades acontecia a partir da utilização do atributo “Activate”. Já o uso de FromServicesAttribute tornou-se necessário a partir do Release Candidate do Visual Studio.

Por fim, na Listagem 5 está um trecho de código que deverá ser incluído na View Index. A ideia com isto será exibir o valor associado às referências resolvidas via injeção de dependência:

  • A cláusula “@inject” será utilizada com a dependência representada pela variável “ObjetoC”, de forma que se associe a esta referência uma instância do tipo TesteInjDependenciaC;
  • Serão usadas ainda as referências de ITesteInjDependenciaA e ITesteInjDependenciaB vinculadas ao objeto ViewBag (devendo-se recordar que as mesmas foram resolvidas a partir de HomeController).
...

@inject TesteInjDependenciaC ObjetoC

<div style="height: 150px; padding-top: 40px;">
    <p>
        <b>Objeto A:</b> @ViewBag.ObjetoA.IdReferencia
    </p>
    <p>
        <b>Objeto B:</b> @ViewBag.ObjetoB.IdReferencia
    </p>
    <p>
        <b>Objeto C:</b> @ObjetoC.IdReferencia
    </p>
</div>

Listagem 5: View Index.cshtml (Controller HomeController)

Teste da aplicação criada

Na Imagem 3 está a tela inicial da aplicação TesteInjDependenciaASPNET5. Em vermelho encontra-se destacado o conteúdo dos três objetos resolvidos via injeção de dependências.

injdepvs2015-03
Imagem 3: Tela inicial da aplicação TesteInjDependenciaVS2015

Ao se proceder com um refresh na tela inicial (Imagem 4) será possível constatar que as dependências foram novamente resolvidas:

  • A instância correspondente a “Objeto A” funciona como um Singleton, logo seu valor permaneceu idêntico àquele apresentado no passo anterior;
  • Já os valores de “Objeto B” e “Objeto C” foram atualizados, visto que novas referências foram associadas às dependências encontradas.

injdepvs2015-04
Figura 4: Novo refresh, com os valores atualizados em destaque

Conclusão

O novo mecanismo de injeção de dependências detalhado neste artigo é mais uma das alterações significativas que integram o ASP.NET 5. A própria arquitetura nesta versão reformulada da plataforma privilegia o uso deste tipo de prática em busca de uma maior performance, uma vez que o acesso a recursos básicos (como cache e configurações de um projeto) acontece somente após a configuração das dependências necessárias em uma aplicação.

Espero que este conteúdo possa ter sido útil.

Até uma próxima oportunidade!

Referências

ASP.NET 5 Documentation
http://docs.asp.net/en/latest/

Inversion of Control Containers and the Dependency Injection pattern
http://martinfowler.com/articles/injection.html

Visual Studio 2015 + ASP.NET 5 (vNext) + C# 6.0 + MVC 6
https://curah.microsoft.com/360343/visual-studio-2015-aspnet-5-vnext-c-60-mvc-6

Renato Groffe

Atua como consultor em atividades voltadas ao desenvolvimento de softwares há mais de 13 anos. Bacharel em Sistemas de Informação, com especialização em Engenharia de Software. Microsoft Certified Technology Specialist (Web, WCF, Distributed Applications, ADO.NET, Windows Forms), Microsoft Specialist (HTML5 with JavaScript and CSS3, Developing ASP.NET MVC 4 Web Applications), Oracle Certified Associate (PL/SQL), Sun Certified (SCJP, SCWCD), ITIL Foundation V2, Cobit 4.1 Foundation.

Facebook Google+ 

Comentários

comentarios