RSS

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

07 nov

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

 

Anúncios
 
5 Comentários

Publicado por em 07/11/2010 em ASP.NET MVC, Design Pattern

 

Tags: , , ,

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

  1. Roberto Gentile

    16/11/2010 at 10:29 am

    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!

     
  2. Rafael Zaccanini

    16/11/2010 at 7:42 pm

    Olá Roberto, muito obrigado pelo feedback! Em breve estarei divulgando mais artigos. Abraços.

     
  3. Alexsandro

    08/01/2011 at 9:50 pm

    Para fazer injeção de dependência geralmente precisamos de uma lib?

    Ninguém hoje implementa sua própria injeção de dependência não?

     

Deixe um comentário

Preencha os seus dados abaixo ou clique em um ícone para log in:

Logotipo do WordPress.com

Você está comentando utilizando sua conta WordPress.com. Sair / Alterar )

Imagem do Twitter

Você está comentando utilizando sua conta Twitter. Sair / Alterar )

Foto do Facebook

Você está comentando utilizando sua conta Facebook. Sair / Alterar )

Foto do Google+

Você está comentando utilizando sua conta Google+. Sair / Alterar )

Conectando a %s

 
%d blogueiros gostam disto: