Cuando comenzamos un nuevo proyecto hay algunas tareas que no están directamente relacionadas con el objetivo mismo de la aplicación, sino que tienen que ver más con la arquitectura. Si queremos código testeable, fácil de mantener y elegante, necesitaremos implementar #IoC (Inversión de Control) y #DI (Inyección de Dependencias).
He creado una pequeña libreria “multi-motor-ioc” que se encargará de registrar los interfaces o servicios por tí, y cualquier clase adicional que necesites con la ayuda de atributos de clase. Soporta de arranque tres de los contenedores IoC más utilizados: Autofac, Unity y nInject, además de permitir implementar cualquier otro que uses o necesites en tu proyecto.
Descarga el código desde mi repositorio en BitBucket con GIT: http://repo.joanvilarino.info/diautoregistrationformvc
O descarga directamente el proyecto en formato ZIP: http://repo.joanvilarino.info/diautoregistrationformvc/downloads
La primera cosa que debes tener en cuenta es que he creado esta librería como ejemplo para una serie de futuros artículos, y que en este momento solamente soporta un pequeño conjunto de funciones, que iré ampliando en el futuro con los siguientes artículos de la serie. Si necesitas algo que no está todavía soportado, eres totalmente libre de implementarlo tú mismo (un pull request sería tremendo) o esperar al “próximo episodio”.
Dicho esto, comenzamos nuestro super impresionante proyecto, y tenemos que enfrentarnos a la inyección de dependencia…
La mayoría de contenedores IoC disponibles tienen extensiones para manejar los proyectos MVC, así que no tienes que registrar ni resolver las clases controlador, pero aún así tendrás que registrar manualmente todas las dependencias de interfaces que esos controladores tengan en su constructor (los servicios que van a utilizar para su funcionamiento).
Cuando trabajas en un proyecto pequeño, eso no es un problema. Siempre puedes añadir un pequeño método en tu clase Startup.cs, y registrar tus interfaces manualmente de esta manera (dependiendo del contenedor IoC que uses, en este ejemplo nInject).
// Inicializar contenedor var _kernel = new StandardKernel(); // Registar tipo con interfaz _kernel.Bind<IMiInterfaz>().To<MiClase>();
Pero ahora imagínate que tienes que hacer un gran proyecto con cientos de interfaces o miles de clases. El métoco podría crecer hasta unas dimensiones inmantenibles.
La pequeña libreria que os propongo, DependencyInjectionServices
, registrará los interfaces por tí, permitiendo manetener un pequeño método que hará “toda la magia”.
Para éste ejemplo, he usado una plantilla MVC5 de Visual Studio 2013 Community Edition (gratuito), así que los packages utilizados son para la version MVC5, incluyendo su IDependencyResolver
para que MVC resuelta sus controladores, pero debería ser fácil cambiar a cualquier otra versión, o otro tipo de proyecto no Web o MVC.
Usando DependencyInjectonService
, todo el proceso de registro debería ser algo tan sencillo como esto:
private void ConfigureSimpleDependencyInjector(IDependencyInjectionEngine engine) { new DependencyInjectionService(engine) .RegisterDependencies(new DependencyInjectionSetting() { AssemblyFilter = new List<string> { "DITest.", "DIRegistrationTest" } }); }
Puedes ver este método (un poco diferente pero casi lo mismo) en la clase Startup.cs del proyecto MVC de ejemplo (te lo has bajado, ¿verdad?)
Entrando en detalle, puedes ver que crea una instancia de nuestro DependencyInjectionService
pasándole el motor IoC engine
que hayamos elegido, y entonces llamando al método RegisterDependencies
con un nuevo objeto de la clase DependencyInjectionSetting
que establecerá las opciones para el registro.
Las opciones que tenemos disponibles para configurar el auto-registro en la clase DependencyInjectionSetting
, por el momento, son las siguientes:
AssemblyFilter (Requerida)
Esto es una lista de strings con los nombres exactos o parciales de los ensamblados que queremos incluir en el rastreo de tipos. Una vez la librería ha conseguido esos ensamblados, buscará en ellos los tipos que han sido “decorados” con el atributo DependencyInjectionRegisterAttribute
, y los registrará en el contenedor IoC para su futura resolución.
ExternalAssemblyFilter (Opcional)
Esta propiedad actua como la anterior, pero esta vez buscara archivos de ensamblado, permitiéndonos registrar tipos que se encuentran en ensamblados no referenciados en nuestro proyecto, que serán implementados de forma dinámica. No olvidéis incluir la extensión .dll o .exe (aquí buscamos archivos…). Estos ensamblados externos, por el momento, deberán estar en la carpeta /bin
de nuestro proyecto.
Si no se especifica, no se añadirán ensamblados externos.
ClassesForcedToRegister (Optional)
Añadiendo tipos a esta lista, forzaremos a la libreria a registarlos. Por ejemplo, si tu motor elegido no puede hacer auto-registro de controladores MVC, necesitarás añadirlos a esta lista de esta manera:
ClassesForcedToRegister = Assembly.GetExecutingAssembly().GetTypes() .Where(t => typeof (Controller).IsAssignableFrom(t)) .ToList()
Seleccionando qué tipos se registarán
Como he dicho antes, la libreria encontrará las clases a registrar por su cuenta. Para conseguir esto, implementa dos clases Attribute
que se usaran para Añadir o Ignorar un tipo determinado, incluyéndolas en la declaración del tipo.
Incluyendo tipos con DependencyInjectionRegister
Si quiere incluir un tipo en el registro, añade este atributo a la declaración del mismo:
[DependencyInjectionRegister] public class MiServicio : IMiServicio { // Métodcos de la clase... }
Si tu IoC no lo hace automáticamente, podrías usar este atributo en todos tus controladores para que fuesen registrados automáticamente.
Excluyendo tipos con DependencyInjectionIgnore
Si añades este atributo a cualquier tipo, la libreria lo ignorará y no será auto-registrado.
Esto es útil en las siguientes situaciones:
- Cuando se añade una clase al contenedor IoC, se comprueban los interfaces que implementa, y cada unos de esos interfaces son emparejados con esta clase. Si no quieres que alguno de ellos sea registrado, añade el atributo en la declaración del interfaz para que sea ignorado.
- Cuando se implementa el mismo interfaz en más de una clase, deberías usar el atributo de manera que ignore las clases que no quieres que se emparejen con él. Si la librería encuentra más de una clase asociada al mismo interfaz, se producirá un error.
- Cuando quieres hacer registro personalizado de alguna clase (cambiar el ciclo de vida del objeto o cualquier otra función no soportada todavía por la librería), puedes añadir este atributo para ignorar la clase y registrarla tú manualmente.
Creando tus controladores
Ahora que tu aplicación está lista para auto-registrar cualquier cosa que le tires, comienza el desarrollo del proyecto en sí, y como dice el “manual del buen programador”, la mejor manera es inyectar todos los servicios en el constructor del controlador, de manera que sean resueltos automáticamente por el contenedor IoC. Aquí va el ejemplo:public class HomeController : Controller { private IExternalService _myService; private ILocalService _anotherService; public HomeController(IExternalService myService, ILocalService anotherService) { // Guardamos los servicios inyectados para usarlos después... _myService = myService; _anotherService = anotherService; } public ActionResult Index() { _myService.TerrificMethodOne(); ViewBag.myServiceResult = _myService.SayHello(); ViewBag.anotherServiceResult = _anotherService.GuessWhat(); return View(); } }
Eso es todo!! No hace falta nada más. Si has marcado todos tus servicios con
DependencyInjectionAttribute
, deberían implementarse recursivamente, y no deberías hacer ni un solonew
en tu aplicación!Muy bien, ya se como se usa, pero … como está hecho?
En los siguientes artículos intentaré explicar algunas de las técnicas usadas para crear esta librería (como usar atributos personalizados, uso de reflection para encontrar interfaces, clases y herencias, como crear e inyectar los “motores” para la librería, etc…). Simplemente mantente informado – como sé que ya haces – en mi blog, y todo será revelado!Gracias!
Realmente agradezco si has llegado hasta tan lejos y aún más si te ha gustado y / o planeas usarlo en un futuro.Visita mi repositorio en BitBucket si quieres ver más proyectos de ejemplo!!
Joan Vilariño
Senior .NET Developer at OhpenLatest posts by Joan Vilariño (see all)
- Factoría de Objetos: Crea objetos con Expression Trees y atributos - 17/02/2016
- Nuevo año, nueva dirección, nuevo hosting! - 02/12/2015
- Usando
dynamic
overloads - 21/10/2015
17/01/2015 a las 18:48
Muy buen artículo y gran blog!!!!
17/01/2015 a las 18:49
Gracias!
19/01/2015 a las 12:28
Muy buen artículo!
Un saludo.