Sin pretender escribir un tutorial se enumeran los pasos llevados a cabo en esta aplicación para realizar una inyección de dependencias sobre un controlador fuertemente acoplado usando el contenedor de inversión de Control (IoC container) Ninject y que servirá de ejemplo para realizar pruebas unitarias posteriores aquí.
La idea es desacoplar un controlador fuertemente acoplado mediante instancia de un contexto con una acción que devuelve una lista de nombres de ciudades en BBDD.
Sistema muy simple donde no podríamos realizar pruebas unitarias debido a el fuerte acoplamiento al instanciar el contexto desde el controlador.
Creamos una tabla llamada "Ciudades" en BBDD con los siguientes campos
Y la llenamos a partir de los datos existentes aquí.
Creamos un Entity Data Model con ADO.NET Entity Data Model a partir de la tabla recién creada.
Con ello ya tendríamos nuestro Contexto que se compone sólo de una Entidad: Ciudades.
Creamos una clase "CiudadViewModel" con las mismas propiedades de la entidad del contexto creado anteriormente
Esta clase nos servirá como item de la lista modelo que envía nuestro controlador a la vista.
using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace proyectoMVC.ViewModels { public class CiudadViewModel { public int ID { get; set; } public string NOMBRE { get; set; } } }
Creamos un MVC 5 Controller "Ciudades" acoplado que instancia el contexto creado anteriormente y devuelve en la accion "DameTodasCiudades" todas las ciudades existentes en BBDD como una lista de CiudadViewModel.
(Aquí hacemos uso de automapper para el mapeo de Entidad del Modelo al ViewModel.)
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using proyectoMVC.Models; using proyectoMVC.ViewModels; using proyectoMVC.ViewModels.Mappers; namespace proyectoMVC.Controllers { public class CiudadesController : Controller { // GET: DameTodasCiudades public ActionResult DameTodasCiudades() { using (DB_9AEE51_Ciudad db = new DB_9AEE51_Ciudad()) { var lstCiudadViewModel = db.Ciudades.ToList() .Select(t => { var r = CiudadMapper.Entity2ViewModel(t); return r; }); return View("~/Views/Ciudades/_DameTodasCiudades.cshtml", lstCiudadViewModel.ToList()); } } } }
Creamos la vista "_DameTodasCiudades" que se "alimentará" del modelo enviado por el controlador "Ciudades" en su acción Index creado en el paso previo y mostrará la tabla de las ciudades en BBDD.
Hasta aquí la navegación a la acción "DameTodasCiudades" del controlador "Ciudades" funcionaría correctamente pero en la siguiente sección vamos realizar algunos cambios.
Para crear un repositorio intermedio entre el contexto creado y el controlador primero definimos la siguiente interfaz:
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace proyectoMVC.Models.Interfaces { public interface ICiudadRepositorio : IDisposable { IListGetAllCiudades(); } }
Ahora sí, creamos el nuevo repositorio el cual implementa la interfaz ICiudadRepositorio.
using System; using System.Collections.Generic; using System.Linq; using System.Web; using proyectoMVC.Models.Interfaces; namespace proyectoMVC.Models { public class EntityCiudadManagerRepository : ICiudadRepositorio { private DB_9AEE51_Ciudad _db = new DB_9AEE51_Ciudad(); public IEnumerableGetAllCiudades() { return _db.Ciudades.ToList(); } } }
Modificamos el controlador para tomar la lista de ciudades a partir del nuevo repositorio.
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using proyectoMVC.Models; using proyectoMVC.ViewModels; using proyectoMVC.ViewModels.Mappers; namespace proyectoMVC.Controllers { public class CiudadesController : Controller { ////ACCION ACOPLADA A CONTEXTO //// GET: DameTodasCiudades //public ActionResult DameTodasCiudades() //{ // using (DB_9AEE51_Ciudad db = new DB_9AEE51_Ciudad()) // { // var lstCiudadViewModel = db.Ciudades.ToList() // .Select(t => // { // var r = CiudadMapper.Entity2ViewModel(t); // return r; // }); // return View("~/Views/Ciudades/_DameTodasCiudades.cshtml", lstCiudadViewModel.ToList()); // } //} // GET: DameTodasCiudades public ActionResult DameTodasCiudades() { using (var EntityCiudadManagerRepositoryX = new EntityCiudadManagerRepository()) { var lstCiudadViewModel = EntityCiudadManagerRepositoryX.GetAllCiudades() .Select(t => { var r = CiudadMapper.Entity2ViewModel(t); return r; }); return View(lstCiudadViewModel); } } } }
Hasta aquí la navegación a la acción "DameTodasCiudades" del controlador "Ciudades" funcionaría correctamente pero en la siguiente sección vamos realizar algunos cambios.
using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.Mvc; using proyectoMVC.Models; using proyectoMVC.ViewModels; using proyectoMVC.ViewModels.Mappers; using proyectoMVC.Models.Interfaces; namespace proyectoMVC.Controllers { public class CiudadesController : Controller { ////ACCION ACOPLADA A CONTEXTO //// GET: DameTodasCiudades //public ActionResult DameTodasCiudades() //{ // using (DB_9AEE51_Ciudad db = new DB_9AEE51_Ciudad()) // { // var lstCiudadViewModel = db.Ciudades.ToList() // .Select(t => // { // var r = CiudadMapper.Entity2ViewModel(t); // return r; // }); // return View("~/Views/Ciudades/_DameTodasCiudades.cshtml", lstCiudadViewModel.ToList()); // } //} ////ACCION ACOPLADA A REPOSITORIO //// GET: DameTodasCiudades //public ActionResult DameTodasCiudades() //{ // using (var EntityCiudadManagerRepositoryX = new EntityCiudadManagerRepository()) // { // var lstCiudadViewModel = EntityCiudadManagerRepositoryX.GetAllCiudades() // .Select(t => // { // var r = CiudadMapper.Entity2ViewModel(t); // return r; // }); // return View("~/Views/Ciudades/_DameTodasCiudades.cshtml", lstCiudadViewModel.ToList()); // } //} private readonly ICiudadRepositorio _ICiudadRespositorio; public CiudadesController(ICiudadRespositorio ICiudadRespositorioX) { _ICiudadRespositorio = ICiudadRespositorioX; } // ACCION DE CONTROLADOR DESACOPLADO // GET: DameTodasCiudades public ActionResult DameTodasCiudades() { var lstCiudadViewModel = _ICiudadRespositorio.GetAllCiudades() .Select(t => { var r = CiudadMapper.Entity2ViewModel(t); return r; }); return View("~/Views/Ciudades/_DameTodasCiudades.cshtml", lstCiudadViewModel.ToList()); } }
Para la inyeccion de dependencias utilizamos el contenedor de inversión de control (IoC Container) Ninject
Añadimos el paquete Ninject.MVC5 usando el Nuget Package Manager al proyecto ASP.NET MVC.
Esto añade 3 referencias a nuestro proyecto: "Ninject.dll", "Ninject.Web.Common.dll" y "Ninject.Web.Mvc.dll", y un archivo NinjectWebCommons.cs en App_Start.
Configuremos Ninject para que pueda inyectar la clase EntityCiudadManagerRepository cuando encuentre ICiudadRepositorio.
Para ello modificamos el Método "RegisterServices" en App_Start/NinjectWebCommons.cs
private static void RegisterServices(IKernel kernel) { // Usar la clase EntityCiudadManagerRepository cuando se encuentre la interface ICiudadRespositorio kernel.Bind<ICiudadRepositorio>().To<EntityCiudadManagerRepository>(); }
Como podemos ver, al navegar a ciudades la vista se genera correctamente.
Y además ya tenemos preparado el controlador para poder realizar pruebas unitarias: Pruebas Unitarias - Moq