ASP.NET MVC – Injeção de dependência com StructureMap

Standard

Injeção de dependência ?

A injeção de dependência é geralmente utilizada quando se deseja diminuir o acoplamento entre diferentes componentes/módulos de um sistema, de forma que o controle de instanciação das classes dependentes é realizado fora das classes, ou seja, todas as dependências entre os componentes/módulos não são definidos programaticamente, mas através da configuração de um container, onde o mesmo é responsável por injetar em cada componente/módulo suas dependências necessárias declaradas no código da classe. Mais claramente, utilizando a injeção de dependência é possível instanciar diversas classes concretas, sem manter-se aclopado a mesma.

Criação do projeto

Vamos criar um projeto do tipo ASP.NET MVC com o nome de Site.Apresentação no respectivo caminho C:Site conforme a figura 1.0.

Figura 1.0

Sem criar um projeto de testes, vamos renomear nossa solução para Site e criar mais dois projetos do tipo Class Library com os nomes de Site.Dados, que será o projeto onde ficara nosso Model e todas as chamadas ao banco de dados, e Site.Servicos, que ficara responsável por disponibilizar os recursos e serviços pertinentes a aplicação, ao final nossa aplicação deve ficar igual a figura 1.1.

Figura 1.1

 

Agora no projeto de Dados, vamos criar uma pasta com o nome de Model, como utilizaremos LinqToSQL, dentro da mesma criamos um arquivo do tipo LINQ to SQL Classes com o nome de SiteDB. Para nosso projeto eu criei uma tabela no banco chamada tbUsuarios, então arrastamos nossa tabela para o arquivo criado e a renomeamos para Usuario, igual à figura 1.2.

Figura 1.2

Ainda no projeto de dados vamos criar uma interface na raiz do projeto com o nome de IUsuarioDB com o seguinte código:

using System.Linq;
using Site.Dados.Model;

namespace Site.Dados
{
    public interface IUsuarioDB
    {
        IQueryable<Usuario> SelecionaUsuarios();
    }
}

 

Em seguida criamos uma classe chamada UsuarioDB que ira implementar a interface criada anteriormente da seguinte forma:

using System.Linq;
using Site.Dados.Model;

namespace Site.Dados
{
    public class UsuarioDB : IUsuarioDB
    {
        SiteDBDataContext _db;

        public UsuarioDB(SiteDBDataContext db)
        { this._db = db; }

        public IQueryable<Usuario> SelecionaUsuarios()
        {
            return _db.Usuarios;
        }

    }

}

 

Note que é criado um construtor que recebe como parâmetro um objeto do tipo SiteDBDataContext onde atualiza o objeto da classe que é utilizado para realizar as consultas no DB.

Agora no projeto de Serviços vamos adicionar uma referencia ao projeto Site.Dados conforme a figura 1.3.

Figura 1.3

Criamos agora uma interface na raiz do projeto com o nome de IUsuarioServ com o seguinte código:

using Site.Dados.Model;

namespace Site.Servicos
{
    public interface IUsuarioServ
    {
        Usuario SelecionaUsuario(int codUsuario);
    }
}

 

Em seguida criamos uma classe chamada UsuarioServ que implementara a interface criada conforme é exibido abaixo:

using Site.Dados.Model;
using Site.Dados;
using System.Linq;

namespace Site.Servicos
{
    public class UsuarioServ : IUsuarioServ
    {
        IUsuarioDB _db;

        public UsuarioServ(IUsuarioDB db)
        { this._db = db; }

        public Usuario SelecionaUsuario(int codUsuario)
        {
            return _db.SelecionaUsuarios()
                      .Where(u => u.codUsuario == codUsuario)
                      .SingleOrDefault();
        }
    }
}

 

Note que também é criado um construtor, porém, este recebe como parâmetro um objeto do tipo IUsuarioDB que atualiza o objeto da classe que é utilizado para chamar os métodos declarados na interface IUsuarioDB do projeto de Dados.

Vamos agora adicionar duas referências no projeto de Apresentação, uma do projeto de Serviços e outra do projeto de Dados.

Em seguida modificaremos o HomeController para que fique da seguinte forma:

using System.Web.Mvc;
using Site.Servicos;
using Site.Dados.Model;

namespace Site.Apresentacao.Controllers
{
    [HandleError]
    public class HomeController : Controller
    {
        IUsuarioServ _serv;

        public HomeController(IUsuarioServ serv)
        { this._serv = serv; }

        public ActionResult Index()
        {
            Usuario u = _serv.SelecionaUsuario(1);
            ViewData["Message"] = u.nome;
            return View();
        }

        public ActionResult About()
        {
            return View();
        }
    }
}

 

Note novamente o construtor que atualiza o objeto da interface IUsuarioServ referente ao projeto de Serviços.

Repare que se compilarmos a aplicação não é exibido nenhum erro, porém, se a executarmos é gerada uma Exception do tipo InvalidOperationException, conforme é mostrado na figura 1.4.

Figura 1.4

Este erro ocorre, pois, como definimos que o construtor do Controller recebera um objeto do tipo IUsuarioServ como parâmetro, algo deve injetar esta dependência ao mesmo, ai entra o StructureMap.

StructureMap

Vamos salvar o arquivo StructureMap.dll no diretório C:Site de nossa aplicação. Agora adicionaremos a referencia da DLL nos projetos de Apresentação e Dados.

Já no projeto de Dados vamos criar uma classe chamada DBServiceRegistry na raiz do projeto com o seguinte conteúdo:

using StructureMap.Configuration.DSL;
using Site.Dados.Model;

namespace Site.Dados
{
    public class DBServiceRegistry : Registry
    {
        protected override void configure()
        {
            ForRequestedType<SiteDBDataContext>()
               .TheDefault
               .Is
               .ConstructedBy(() => new SiteDBDataContext());
        }
    }
}

 

Novamente no projeto de Apresentação dentro do diretório View vamos criar duas classes, uma chamada Bootstrapper com o conteúdo:

using StructureMap;
using StructureMap.Configuration.DSL;
using Site.Dados;
using Site.Servicos;
using Site.Dados.Model;

namespace Site.Apresentacao
{
    public static class Bootstrapper
    {
        public static void ConfigureStructureMap()
        {
            ObjectFactory.Initialize(structureMap =>
            {
                structureMap.AddRegistry(new DBServiceRegistry());
                structureMap.AddRegistry(new SiteRegistry());
            });
        }
    }
    public class SiteRegistry : Registry
    {
        protected override void configure()
        {
            ForRequestedType<IUsuarioDB>()
            .TheDefaultIsConcreteType<UsuarioDB>();
            ForRequestedType<IUsuarioServ>()
            .TheDefaultIsConcreteType<UsuarioServ>();
        }
    }
}

 

E outra chamada ViewEngine com o conteúdo:

using System.Web.Mvc;

namespace Site.Apresentacao.Views
{
    public class ViewEngine : WebFormViewEngine
    {
        public ViewEngine()
        {
            MasterLocationFormats = new[] {
               "~/Views/{1}/{0}.master",
               "~/Views/Shared/{0}.master"
            };

            ViewLocationFormats = new[] {
               "~/Views/{1}/{0}.aspx",
               "~/Views/{1}/{0}.ascx",
               "~/Views/Shared/{0}.aspx",
               "~/Views/Shared/{0}.ascx",
            };

            PartialViewLocationFormats = ViewLocationFormats;

        }
    }
}

 

Desta vez dentro do diretório Controllers criamos outra classe, agora chamada StructureMapControllerFactory com o seguinte código implementado:

using System;
using System.Web.Mvc;
using StructureMap;

namespace Site.Apresentacao.Controllers
{
    public class StructureMapControllerFactory : DefaultControllerFactory
    {
        protected override IController GetControllerInstance(Type controllerType)
        {
            IController result = null;

            if (controllerType != null)
            {
                try
                {
                    result = ObjectFactory.GetInstance(controllerType) as System.Web.Mvc.Controller;
                }
                catch (StructureMapException)
                {
                    System.Diagnostics.Debug.WriteLine(ObjectFactory.WhatDoIHave());
                    throw;
                }
            }
            return result;
        }
    }
}

 

E por fim alteramos o método Application_Start do arquivo Global.asax para que fique da seguinte forma:

protected void Application_Start()
{
   RegisterRoutes(RouteTable.Routes);
   Bootstrapper.ConfigureStructureMap();
   ControllerBuilder.Current.SetControllerFactory
       (new Site.Apresentacao.Controllers.StructureMapControllerFactory());

   ViewEngines.Engines.Add(new Views.ViewEngine());
}

 

Agora sim. Vamos iniciar nossa aplicação e note que, desta vez é exibido o nome do usuario enviado como parametro pelo controller até o projeto de serviço, que por sua vez chama o projeto de dados e retorna o usuário em questão.

Figura 1.5

Conclusão

Já sabemos que, quanto maior o acoplamento entre os componentes do projeto, maior é a dependência entre um módulo e outro, que pode resultar em alterações e manutenções em diversas partes do sistema. Obviamente que, o desejável é que o acoplamento seja o menor possível entre os componentes, para que, quando efetuada qualquer alteração em código tenha o menor impacto possível nas classes dependentes e vice versa.

Espero ter ajudado a perceber como a injeção de dependência é um ótimo recurso para diminuirmos o acoplamento em um projeto.

Baixe o projeto: http://www.4shared.com/file/-GiWvIlF/Projeto.html

Até a próxima !!

Rafael Zaccanini

rafael.zaccanini@gmail.com

5 thoughts on “ASP.NET MVC – Injeção de dependência com StructureMap

  1. Roberto Gentile

    Rafael, parabéns pelo artigo, não poderia tê-lo feito melhor. Espero que continue com os posts, eu os irei ler com certeza. Abraços!

Leave a Reply

Your email address will not be published. Required fields are marked *