ASP.NET MVC 3 y Structure Map

Como ya hemos hablado en otros post, una de las características que nos permiten mejorar el código de nuestros desarrollos es la utilización de patrones de diseño. Uno de esos patrones es la Inyección de Dependencias que consiste en suministrar objetos a una clase en lugar de ser la propia clase quien cree el objeto.

En este post voy a mostrarles un framework llamado Structure Map que nos permite aplicar este patrón en nuestros proyectos .NET y la forma de integrarlo al soporte de IoC que provee ASP.NET MVC 3.

Structure Map es un framework para .NET que nos permite trabajar con Inyección de Dependencias e Inversión de Control.

El primer paso sera agregar las referencias a la librería de Structure Map. Para ello vamos a instalarlas utilizando Nuget:

Agregando la librería Structure Map

Agregando la librería Structure Map

Es importante recordar que la pieza central respecto al soporte de IoC que provee ASP.NET MVC 3 es la interfaz IDependencyResolver que nos permite “conectar” distintos motores de IoC con tan solo implementarla en una clase y luego registrarla.

En nuestro ejemplo vamos a crear nuestro propio Dependecy Resolver que va a ser manejado con Structure Map. Para ellos vamos a crear la clase StructureMapDependencyResolver que, como dijimos anteriormente, implementará IDependencyResolver:

1: public class StructureMapDependencyResolver : IDependencyResolver
2: {
3:     private IContainer container;
4:
5:     public StructureMapDependencyResolver(IContainer container)
6:     {
7:         this.container = container;
8:     }
9:
10:     public object GetService(Type serviceType)
11:     {
12:         if (serviceType.IsAbstract || serviceType.IsInterface)
13:         {
14:             return this.container.TryGetInstance(serviceType);
15:         }
16:         else
17:         {
18:             return this.container.GetInstance(serviceType);
19:         }
20:     }
21:
22:     public IEnumerable<object> GetServices(Type serviceType)
23:     {
24:         return this.container.GetAllInstances<object>()
25:             .Where(s => s.GetType() == serviceType);
26:     }
27: }

Lo que hicimos aquí fue implementar los siguiente métodos:

En ambos métodos hemos utilizado el contedor IContainer (que nos provee la librería Structure Map) cuya finalidad es la de registrar y resolver las dependencias que hayamos definido.

Bien, ahora vamos a crear una interfaz bien simple, la cual denominaremos IMessage:
1: public interface IMessage
2: {
3:     string GetMessage();
4: }

Y dos implementaciones bien sencillas de la misma, la primera la llamaremos HelloMessage:

1: public class HelloMessage : IMessage
2: {
3:     public string GetMessage()
4:     {
5:         return "Buenos días!" ;
6:     }
7: }

Y la segunda ByeMessage:

1: public class ByeMessage : IMessage
2: {
3:     public string GetMessage()
4:     {
5:         return "Hasta mañana!" ;
6:     }
7: }

Llego el momento de registrar a nuestro “resolvedor(?) de dependencias”  🙂 y para ello debemos agregar las siguientes lineas en el archivo global.asax:

1: public class MvcApplication : System.Web.HttpApplication
2: {
3:     public static void RegisterGlobalFilters(GlobalFilterCollection filters)
4:     {
5:         filters.Add(new HandleErrorAttribute());
6:     }
7:
8:     public static void RegisterRoutes(RouteCollection routes)
9:     {
10:         routes.IgnoreRoute("{resource}.axd/{*pathInfo}" );
11:
12:         routes.MapRoute(
13:             "Default", // Route name
14:             "{controller}/{action}/{id}", // URL with parameters
15:             new { controller = "Home" , action = "Index" } 
16:         );
17:
18:     }
19:
20:     protected void Application_Start()
21:     {
22:         AreaRegistration .RegisterAllAreas();
23:
24:         RegisterGlobalFilters(GlobalFilters.Filters);
25:         RegisterRoutes(RouteTable.Routes);
26:
27:         InitializeContainer();
28:     }
29:
30:     public void InitializeContainer()
31:     {
32:         var c = InitContainer();
33:         DependencyResolver.SetResolver(new StructureMapDependencyResolver(c));
34:     }
35:
36:     private static IContainer InitContainer()
37:     {
38:         var container = new Container();
39:
40:         container.Configure(s => s.For<IMessage>().Use<HelloMessage>());
41:
42:         return container;
43:     }
44: }

Dentro del método Application_Start() invocamos un nuevo método llamado InitializeContainer() en el cual vamos a registrar StructureMapDependencyResolver como el encargado de resolver las dependencias. Previamente a este paso hemos creamos el método InitContainer() en el cual hemos creado un objeto Container al cual le fuimos configurando el mapeo entre interfaces y clases que sera utilizado para poder resolver las dependencias (en el ejemplo solo es uno y es el de IMessage).

Es hora de crear el controlador HomeController cuyo constructor recibirá como parámetro un IMessage el cual será “inyectado” cada vez que sea instanciado (de lo cual ya no debemos preocuparnos gracias a IDependencyResolver). También vamos a definir un método de acción Index:

1: public class HomeController : Controller
2: {
3:     private IMessage MessageService { get ; set ; }
4:
5:     public HomeController(IMessage service)
6:     {
7:         this .MessageService = service;
8:     }
9:
10:     public ActionResult Index()
11:     {
12:         ViewBag.Message = MessageService.GetMessage();
13:
14:         return View();
15:     }
16: }

Ejecutamos y veremos que el servicio instancia es el que hemos definido al momento de crear el contenedor, en este caso HelloMessage:

Resultado obtenido de HelloMessage

Resultado obtenido de HelloMessage

Ahora modificamos el contenedor para instanciar el servicio ByeMessage en la interfaces IMessage:

1: private static IContainer InitContainer()
2: {
3:     var container = new Container();
4:
5:     container.Configure(s => s.For<IMessage>().Use<ByeMessage>());
6:
7:     return container;
8: }

Ejecutamos y verificamos que se invoco el servicio que definimos:

Resultado obtenido con ByeMessage

Resultado obtenido con ByeMessage

De más esta decir, que esto simplifica y mejora el código de nuestros desarrollos, ya que en el caso de que debamos modificar la referencias a nuestros servicios, y si estos mantienen el contrato, solo debemos modificar una sola línea de nuestro proyecto, y es el mapeo entre interfaz y clase que hemos configurado en el contenedor de dependencias.

Quienes quieran descargar el proyecto pueden hacerlo desde acá: MvcAndStructureMap.rar.

Espero que les sea de utilidad.

Anuncios

8 comentarios en “ASP.NET MVC 3 y Structure Map

  1. Hola Sebis, muy bueno el post pero te queria consultar cual es el beneficio de aplicar esta técnica, la verdad que no se mucho pero lo poco que veo es bastante código para hacer una sola clase que tiene un metodo, imagínate una clase que tiene muchas propiedades y que a su vez tenga varios métodos, desde ya hablo desde mi inexperiencia, solo que estaba realizando tu ejemplo y me tope con esta consulta, veo que el ahorro de new es bueno pero es como medio largo.

    • Hola Moisés, antes que nada agradezco que te haya gustado el post. Los beneficios de aplicar IoC (como el resto de los principios SOLID) en tus proyectos son varios, pero los principales son:

      – El sistema es más flexible, un cambio no afecta a las otras partes del sistema: si modifico la implementación de un servicio por otro, solo tengo que hacerlo en un único lugar.
      – El sistema debería ser más robusto, ante un cambio, el sistema debería seguir funcionando: si yo modifico un servicio por otro, el sistema debería seguir funcionando, ya que ambos implementaron el mismo contrato.
      – El sistema podría ser reutilizado, siempre y cuando se respeten las firmas de las interfaces.

      Por lo tanto la utilización del patrón de diseño IoC nos permitirá abstraer los módulos de más alto nivel de los de más bajo nivel, esto gracias a que el “Container” se encargará de resolver dichas dependencias. En el ejemplo parecen demasiadas lineas para una sola dependencia, pero a medida que el proyecto crece, la cantidad de dependencias crecen y los beneficios también. 🙂

      Espero que haya aclaro tu duda, y cualquier cosa seguimos en contacto.

      Abrazos!

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s