ASP .NET MVC 6 -   Usando a Injeção de dependência nativa (DI)


 Neste artigo vou mostrar como usar a injeção de dependência(DI) em aplicações ASP .NET MVC 6. (Estou usando a ASP .NET Core 1.0 RC2)

Tecnicamente falando, DI ou Dependency Injection, é definida como um padrão de projeto de software que implementa a inversão de controle (IoC) para resolver dependências entre objetos.

A dependência é um objeto (ou serviço) o qual é passado como dependência para a aplicação cliente. A utilização deste padrão de projeto faz com que os componentes fiquem fracamente acoplados e pode ser implementado de maneira bem simples.

Funciona assim...

Quando um objeto A deseja acessar métodos de um objeto B, então ao invés de instanciar diretamente o objeto B no objeto A, o objeto B é injetado como uma dependência. Neste caso o objeto B implementa uma interface a qual é passada para o objeto A. Dai, o objeto B não é instanciando usando a palavra chave new no objeto A.

A vantagem dessa abordagem é que se no futuro o objeto B mudar, o objeto A não será afetado diretamente. Temos assim objetos levemente acoplados o que é uma boa prática.

Podemos realizar a injeção de dependência de diversas formas e geralmente usamos um framework como o NUnity ou o Ninject, dentre outros,  para realizar a injeção.

A injeção de dependência com ASP .NET Core no MVC 6

A novidade é que com a ASP .NET MVC 6 temos agora um mecanismo nativo para realizar a injeção de dependência sem precisar usar frameworks.(Embora você possa continuar a fazer a DI usando os frameworks)

Nota: Na abordagem ASP .NET MVC 6 um tipo a ser injetado é chamado de um serviço.

A estrutura de injeção de dependência do ASP.NET MVC 6 faz duas tarefas básicas para você:

Há quatro modos de vida para um serviço que está sendo injetado:

  1. Singleton : Um objeto do serviço é criado e fornecido para todas as requisições. Assim, todas as requisições obtém o mesmo objeto;
  2. Escope : Um objeto do serviço é criado para cada requisição. Dessa forma, cada requisição obtém uma nova instância do serviço;
  3. Transient : Um objeto do serviço é criado toda a vez que um objeto for requisitado;
  4. Instance : Você é responsável por criar um objeto do serviço. O framework de DI então usa esse instância em um modo Singleton;

Vamos ver como cada um desses modos funcionam na prática em uma aplicação ASP .NET MVC 6.

Recursos usados

Criando o projeto no VS Community 2015

Abra o VS Community 2015  e clique em New Project;

Selecione Visual C# -> Web;

Escolha o template ASP .NET Core Web Application (.NET Core) e informe o nome Mvc6_InjecaoDependencia e clique em OK;

Selecione o template Empty e clique em OK:

Será criada uma solução com estrutura exibida abaixo:

Observe que não temos na estrutura criada as pastas Models, Views e Controllers, e, se precisarmos teremos que criar essas pastas no projeto.

Configurando o ambiente

Se você expandir a guia References do projeto e visualizar o conteúdo do arquivo project.json vai verificar que temos as seguintes referências:

Note que ainda não temos uma referência a biblioteca do MVC e precisamos incluir essa referência.

Abra o arquivo Startup.cs e no método ConfigureServices() digite : services.AddMvc()

A seguir selecione : Add package Microsoft.AspNetCore.Mvc 1.0.0-rc2-final para incluir a referência ao pacote MVC:

Vamos agora criar as pastas Servicos, Controllers Views no projeto:

Para isso selecione o projeto e no menu Project clique em New Folder e informe o nome de cada pasta:

Dentro da pasta Views vamos criar outra pasta chamada Home usando o mesmo procedimento. A final teremos a seguinte estrutura do projeto:

Observe também a referência à biblioteca Mvc no arquivo project.json.

Definindo um serviço para a injeção de dependência (DI)

Vamos definir um serviço bem simples para que possamos mostrar a injeção de dependência na ASP .NET MVC 6.

Vamos criar um serviço para gerar um identificador único universal usando a estrutura Guid e o método NewGuid() para criar uma nova instância da Guid.

Na pasta Servicos inclua uma interface chamada IServicoGuid via menu Project -> Add New Item -> Interface,  com o código abaixo:

 public interface IServicoGuid
 {
        string IDUnico { get; set; }
 }

Nada de mais, definimos apenas uma propriedade chamada IDUnico.

Agora vamos incluir na mesma pasta uma classe chamada ServicoGuid que vai implementar essa interface com o código abaixo:

using System;
namespace Mvc6_InjecaoDependencia.Servicos
{
    public class ServicoGuid : IServicoGuid
    {
        public string IDUnico { get; set; }
        public ServicoGuid()
        {
            this.IDUnico = Guid.NewGuid().ToString();
        }
    }
}

Definimos no construtor da classe a geração do identificador único usando o método NewGuid() e convertendo para string.

Pois bem esse será o serviço que iremos injetar em nossa aplicação ASP .NET MVC 6.

Criando o controlador HomeController

Clique com o botão direito do mouse sobre a pasta Controllers e a seguir em Add New Item;

Selecione a guia ASP .NET e a opção MVC Controller Class e clique no botão Add;

O controlador será criado com o método Action Index() que irá tratar a requisição GET.

Inclua o código abaixo em azul no controlador HomeController :

using Microsoft.AspNetCore.Mvc;
using Mvc6_InjecaoDependencia.Servicos;
namespace Mvc6_InjecaoDependencia.Controllers
{
    public class HomeController : Controller
    {
        private IServicoGuid servico;
        public HomeController(IServicoGuid _servico)
        {
            this.servico = _servico;
        }
        // GET: /<controller>/
        public IActionResult Index()
        {
            ViewBag.Mensagem = "Usando Injeção de Dependência com ASP .NET MVC 6";
            ViewBag.GUID = this.servico;
            return View();
        }
    }
}

O código acima define uma variável servico do tipo IServicoGuid e no construtor do controlador temos a injeção da instância do serviço representada por _servico e que iremos configurar mais adiante.

O método Action Index() usa o serviço injetado e obtém o identificado global único gerado.

Agora vamos definir a rota padrão da aplicação no arquivo Startup.cs no interior do método Configure:

        public void Configure(IApplicationBuilder app)
        {
            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }

Assim a rota padrão define o controlador Home e o método Index para executarem quando a aplicação iniciar.

Criando a View Index.cshtml na pasta Views/Home

Agora clique com o botão direito sobre a pasta Views/Home e a seguir em Add New Item;

Selecione a guia ASP.NET e a opção MVC View Page e clique em Add;

Será criado o arquivo Index.cshtml na pasta Views/Home. Altere o código do arquivo conforme abaixo:

<html>
<head>
    <title>Minha Primeira Aplicação ASP .NET MVC 6</title>
</head>
<body>
    <h1>Macoratti .net</h1>
    <hr />
    <h4>@ViewBag.Mensagem</h4>
    <br />
    <h3>Identificador Global Único Gerado : @ViewBag.GUID.IDUnico</h3>
</body>
</html>

Esta view vai exibir o resultado do serviço criado.

Aplicando a injeção de dependência

Para realizar o mapeamento das dependências a serem resolvidas precisamos informar qual a interface e a respectiva classe definidas para o nosso serviço.

Isso é feito no método ConfigureServices da classe Startup onde vamos incluir o código a seguir:

   public void ConfigureServices(IServiceCollection services)
   {
            services.AddMvc();
            //services.AddScoped<IServicoGuid, ServicoGuid>();
            //services.AddTransient<IServicoGuid, ServicoGuid>();
            services.AddSingleton<IServicoGuid, ServicoGuid>();
   }

Note que estamos injetando o serviço usando os modos de vida Singleton, Scoped e Transient.

A classe ConfigureServices() recebe como parâmetro uma referência do tipo IServiceCollection que será usada para especificar as dependências do projeto.

No nosso exemplo será registrada a classe ServicoGuid como um tipo de serviço.

1- Usando o modo Singleton

Vamos iniciar testando o AddSingleton que vai registrar o serviço usando o modo Singleton e a seguir os demais tipos de modo de vida do serviço.

Se executarmos agora nosso projeto iremos obter o seguinte resultado :

Se você abrir outra guia do navegador e acionar a URL localhost:1894 vai obter o mesmo resultado confirmando que o objeto Singleton esta sendo criado.

2- Usando o modo Scoped

No método ConfigureServices da classe Startup altere o código conforme abaixo:

   public void ConfigureServices(IServiceCollection services)
   {
            services.AddMvc();
            services.AddScoped<IServicoGuid, ServicoGuid>();
            //services.AddTransient<IServicoGuid, ServicoGuid>();
           //services.AddSingleton<IServicoGuid, ServicoGuid>();
   }

Agora estamos usando o modo Scoped.

Executando o projeto em diversas instâncias do navegador iremos obter o seguinte resultado:

Observe que para cada instância obtemos um número diferente para o Guid o que indica que cada requisição esta sendo fornecida por uma nova instância do objeto.

3- Usando o modo Transient

No método ConfigureServices da classe Startup altere o código conforme abaixo:

   public void ConfigureServices(IServiceCollection services)
   {
            services.AddMvc();
           // services.AddScoped<IServicoGuid, ServicoGuid>();
           services.AddTransient<IServicoGuid, ServicoGuid>();
           //services.AddSingleton<IServicoGuid, ServicoGuid>();
   }

Agora estamos usando o modo Transient.

Para simular a múltipla criação de objetos em um único ciclo request-response vamos alterar o código do controlador HomeController conforme a seguir:

 public class HomeController : Controller
    {
        private IServicoGuid servico1;
        private IServicoGuid servico2;
        public HomeController(IServicoGuid _servico1,  IServicoGuid _servico2)
        {
            this.servico1 = _servico1;
            this.servico2 = _servico2;
        }
        // GET: /<controller>/
        public IActionResult Index()
        {
            ViewBag.Mensagem = "Usando Injeção de Dependência com ASP .NET MVC 6";
            ViewBag.GUID1 = this.servico1;
            ViewBag.GUID2 = this.servico2;
            return View();
        }
    }

Precisamos alterar também a view Index.cshtml para exibir o resultado conforme o código abaixo:

<html>
<head>
    <title>Minha Primeira Aplicação ASP .NET MVC 6</title>
</head>
<body>
    <h1>Macoratti .net</h1>
    <hr />
    <h4>@ViewBag.Mensagem</h4>
    <br />
    <h3> Serviço 1 - Identificador Global Único Gerado : @ViewBag.GUID1.IDUnico</h3>
    <h3> Serviço 2 - Identificador Global Único Gerado : @ViewBag.GUID2.IDUnico</h3>
</body>
</html>

Executando o projeto novamente teremos :

4- Usando o modo Instance

Neste modo temos que criar uma instância do serviço ServicoGuid e fazer o registro com o framework DI.

Para fazer isso altere o código do método ConfigureServices() da classe startup.cs conforme abaixo:

 public void ConfigureServices(IServiceCollection services)
 {
            services.AddMvc();
            //services.AddScoped<IServicoGuid, ServicoGuid>();
            //services.AddTransient<IServicoGuid, ServicoGuid>();
            //services.AddSingleton<IServicoGuid, ServicoGuid>();
            ServicoGuid obj = new ServicoGuid();
            obj.IDUnico = "013f98e7-bb8b-4d9f-b5a0-04e930db88457";
            services.AddInstance<IServicoGuid>(obj);
   }

Criamos um objeto de ServicoGuid e atribuímos à sua propriedade IDUnico um valor. Assim criamos manualmente uma instância do objeto e então registramos usando o método AddInstance().

Executando o projeto com os ajustes na View veremos que ele se comporta como o modo Singleton.

Dessa forma vimos como realizar a injeção de dependência usando os recursos nativos da ASP .NET MVC 6.

Pegue o projeto completo aqui : Mvc6_InjecaoDependencia.zip

Aquele que nem mesmo a seu próprio Filho poupou, antes o entregou por todos nós, como nos não dará também com ele todas as coisas?
Romanos 8:32

Veja os Destaques e novidades do SUPER DVD Visual Basic (sempre atualizado) : clique e confira !

Quer migrar para o VB .NET ?

Quer aprender C# ??

Quer aprender os conceitos da Programação Orientada a objetos ?

Quer aprender o gerar relatórios com o ReportViewer no VS 2013 ?

Referências:


José Carlos Macoratti