webapi unity mvc injection dependency control container c# asp.net-mvc dependency-injection inversion-of-control unity-container

c# - unity - web api inversion of control



Controlador base de inyección de constructor en ASP.NET MVC con Unity (3)

Tengo un controlador base en mi proyecto MVC 5 que implementa alguna funcionalidad compartida. Esta funcionalidad requiere algunas dependencias. Estoy usando Unity 3 para inyectar estas implementaciones en mis controladores, un patrón que ha funcionado bien hasta que cambié mis controladores para heredar de este controlador base. Ahora me estoy topando con el siguiente problema:

public class BaseController : Controller { private readonly IService _service; public BaseController(IService service) { _service = service; } } public class ChildController : BaseController { private readonly IDifferentService _differentService; public ChildController(IDifferentService differentService) { _differentService = differentService; } }

Esto es comprensible que el error de ''BaseController'' does not contain a constructor that takes 0 arguments . Unity no está resolviendo la construcción del BaseController, por lo que no puede inyectar las dependencias en él. Veo dos formas obvias de resolver este problema:

1.) Llame explícitamente al controlador BaseController y haga que cada controlador ChildController inyecte las dependencias del Controlador Base

public class ChildController : BaseController { private readonly IDifferentService _differentService; public ChildController(IDifferentService differentService, IService baseService) : base(baseService) { _differentService = differentService; } }

No me gusta este enfoque por varias razones: una, porque los ChildControllers no están haciendo uso de las dependencias adicionales (por lo que causa que el constructor se infla en los controladores secundarios sin ninguna razón) y, lo que es más importante, si alguna vez cambio la firma del constructor del controlador base, tengo que cambiar las firmas de constructor de cada controlador secundario.

2.) Implementar las dependencias de BaseController mediante inyección de propiedad.

public class BaseController : Controller { [Dependency] public IService Service { get; set; } public BaseController() { } }

Me gusta más este enfoque: no estoy usando ninguna de las dependencias en el código constructor del Controlador Base, pero hace que la estrategia de inyección de dependencias del código sea inconsistente, lo que tampoco es ideal.

Probablemente haya un enfoque aún mejor que implique algún tipo de función de resolución de dependencia de BaseController que solicite al contenedor de Unity que resuelva la firma del método de ctor, pero antes de escribir algo demasiado complicado, me preguntaba si alguien había resuelto este problema anteriormente. Encontré algunas soluciones flotando en la web, pero eran soluciones alternativas, como el Localizador de servicios, que no quiero usar.

¡Gracias!


Con ASP.Net 5 y está construido en DI

public class BaseController : Controller { protected ISomeType SomeMember { get; set; } public BaseController(IServiceProvider serviceProvider) { //Init all properties you need SomeMember = (SomeMember)serviceProvider.GetService(typeof(ISomeMember)); } } public class MyController : BaseController { public MyController(/*Any other injections goes here*/, IServiceProvider serviceProvider) : base(serviceProvider) {} }

ACTUALIZAR

También hay un método de extensión en Microsoft.Extensions.DependencyInjection para hacerlo más corto

SomeMember = serviceProvider.GetRequiredService<ISomeMember>();


He tomado un enfoque algo diferente (pero para mí, bastante obvio y probablemente común). Funciona para mí, pero puede haber errores que no conozco.

He creado propiedades de servicio comunes en mi clase BaseController que utilizan la mayoría de mis controladores. Se instancian cuando son necesarios / referenciados.

Si hay un servicio que necesita un controlador en particular que se usa menos, lo inyecto en el constructor de ese controlador de manera normal.

using JIS.Context; using JIS.Managers; using JIS.Models; using JIS.Services; using System; using System.Collections.Generic; using System.Web; using System.Web.Mvc; namespace JIS.Controllers { public class BaseController : Controller { public BaseController() { ViewBag.User = UserManager?.User; } private IUserManager userManager; public IUserManager UserManager { get { if (userManager == null) { userManager = DependencyResolver.Current.GetService<IUserManager>(); } return userManager; } set { userManager = value; } } private ILoggingService loggingService; public ILoggingService LoggingService { get { if (loggingService == null) { loggingService = DependencyResolver.Current.GetService<ILoggingService>(); } return loggingService; } set { loggingService = value; } } private IUserDirectory userDirectory; public IUserDirectory UserDirectory { get { if (userDirectory == null) { userDirectory = DependencyResolver.Current.GetService<IUserDirectory>(); } return userDirectory; } set { userDirectory = value; } } private ApplicationDbContext appDb; public ApplicationDbContext AppDb { get { if (appDb == null) { appDb = new ApplicationDbContext(); } return appDb; } set { appDb = value; } } protected override void Dispose(bool disposing) { if (disposing && appDb != null) { appDb.Dispose(); } base.Dispose(disposing); } } }

En mi controlador simplemente subclase de este BaseController:

public class UsersController : BaseController { // and any other services I need are here: private readonly IAnotherService svc; public UsersController(IAnotherService svc) { this.svc = svc; } ... }

De esta manera, los servicios comunes se generan sobre la marcha cuando se necesitan, y están disponibles para mis controladores sin una gran cantidad de repeticiones.


Lo primero que debes entender es que no estás creando una instancia del controlador base. Está creando una instancia del controlador secundario, que hereda la interfaz y la funcionalidad de los controladores básicos. Esta es una distinción importante. Cuando dices "los controladores de niños no están haciendo uso de las dependencias adicionales", entonces estás absolutamente equivocado. Porque el ChildController ES el BaseController también. No hay dos clases diferentes creadas. Solo una clase que implementa ambas funcionalidades.

Por lo tanto, dado que ChildController es un controlador base, no hay nada malo ni extraño en pasar parámetros en el constructor de controladores secundarios que llama al constructor de clases base. Esta es la forma en que debe hacerse.

Si cambia su clase base, es probable que tenga que cambiar las clases de sus hijos de todos modos. No hay forma de utilizar la inyección de constructor para inyectar dependencias de clase base que no están incluidas en la clase secundaria.

No recomiendo la inyección de propiedades, ya que esto significa que sus objetos pueden crearse sin la inicialización adecuada, y debe recordar configurarlos correctamente.

Por cierto, los términos adecuados son Subclase y Superclase. Un "niño" es una subclase, el padre es la "superclase".