Cómo agregar inyección de dependencia (DI) usando Unity MVC 4 en una clase sin controlador

Dec 18 2020

Entonces estoy usando Unity MVC-4 para lograr la inyección de dependencia y funciona muy bien con mis Controllerclases, pero tan pronto como intento inyectar en mi clase sin controlador, obtengo el NullReferenceExceptiony puedo ver que mis objetos inyectados no son inicializados por el marco . Te daré las clases correspondientes que estoy usando:

Controller clase (DI funciona):

public class HomeController : Controller
{
    IMyService _myService;

    #region CTOR
    public HomeController(IMyService myService)
    {
        _myService = myService;
    }
    #endregion
    
    public string GetMyString()
    {
        string mystring=string.Empty;
        
        try
        {
            mystring = _myService.GetMyStringFromDLL();
        }
        catch (Exception ex)
        {
            StringBuilder str = new StringBuilder();
            str.AppendLine("Exception in method GetMyString, Error msg: " + ex.Message);
            WriteLog(sb);
        }
        return mystring;
    }
}

Y si hago lo mismo en un método sin controlador (DI no funciona aquí), obtengo un NullReferenceException:

public inteface IMyLogic
{
    string GetMyString();
}

public class MyLogic: IMyLogic
{
    IMyService _myService;

    #region CTOR
    public MyLogic(IMyService myService)
    {
        _myService = myService;
    }
    #endregion
    
    public string GetMyString()
    {
        string mystring=string.Empty;
        
        try
        {
            mystring = _myService.GetMyStringFromDLL();  //Getting NullReferenceException here
        }
        catch (Exception ex)
        {
            StringBuilder str = new StringBuilder();
            str.AppendLine("Exception in method GetMyString, Error msg: " + ex.Message);
            WriteLog(sb);
        }
        return mystring;
    }
}

Mi BootStrapper.csclase se ve así:

public static class Bootstrapper
{

    public static IUnityContainer Initialise()
    {
      var container = BuildUnityContainer();
        container.RegisterType<IMyService , MyService>();
        container.RegisterType<IMyLogic, MyLogic>(new HierarchicalLifetimeManager());
        DependencyResolver.SetResolver(new UnityDependencyResolver(container));

      return container;
    }

    private static IUnityContainer BuildUnityContainer()
    {
      var container = new UnityContainer();  
      RegisterTypes(container);
      return container;
    }

    public static void RegisterTypes(IUnityContainer container)
    {

    }

}

Si ve arriba en la línea container.RegisterType<IMyService , MyService>();, la interfaz y su implementación concreta están en un módulo separado.

Y mi Global.asax.cses:

protected void Application_Start()
{
    Bootstrapper.Initialise();
    AreaRegistration.RegisterAllAreas();
    GlobalFilters.Filters.Add(new OfflineActionFilter());
    RouteConfig.RegisterRoutes(RouteTable.Routes);
    BundleConfig.RegisterBundles(BundleTable.Bundles);
}

¿Cómo puedo inyectarme IMyServiceen MyLogicclase?

Respuestas

4 Jackdaw Dec 21 2020 at 20:20

Use el atributo [InjectionConstructor] para decirle a Unity que la MyLogicclase depende de un objeto que se inyectará en el constructor:

[InjectionConstructor]
public MyLogic(IMyService myService)
{
   _myService = myService;
}

En realidad, [InjectionConstructor]se recomienda su uso cuando la clase inyectada contiene más de un constructor. Por tanto, el atributo se utiliza para resolver la desambiguación. Era solo mi hipótesis por qué Unityno se puede resolver el tipo requerido, debido a que el código de la pregunta no contiene todas las partes del código. Pero en el código de prueba a continuación, el [InjectionConstructor]atributo no es necesario, porque solo se declara un constructor.

Aquí está el código de prueba.

La IMyServicedefinición de interfaz:

public interface IMyService
{
    string GetMyStringFromDLL();
}

La ILogicdefinición de interfaz:

public interface IMyLogic
{
    string GetMyString();
}

La MyLogicimplementación:

public class MyLogic : IMyLogic
{
    IMyService _myService;
       
    public MyLogic(IMyService myService)
    {
        _myService = myService;
    }
        
    public string GetMyString()
    {
        var mystring = string.Empty;
        try
        {
            mystring = "MyLogic.GetMyString() -> " + _myService.GetMyStringFromDLL();
        }
        catch (Exception ex)
        {                
            System.Diagnostics.Debug.WriteLine("Exception in method MyLogic.GetMyString(): " + ex.Message); 
        }
        return mystring;
    }
}

La MyServiceimplementación:

public class MyService : IMyService
{
    public string GetMyStringFromDLL()
    {
        return "MyService.GetMyStringFromDLL() is called.";
    }
}

La Bootstrapperinicialización:

public static class Bootstrapper
{
    public static IUnityContainer Initialise()
    {
        var container = new UnityContainer();
        container.RegisterType<IMyService, MyService>();            
        container.RegisterType<IMyLogic, MyLogic>(new HierarchicalLifetimeManager()); 
        DependencyResolver.SetResolver(new UnityDependencyResolver(container));
        return container;
    }
}

La Homeimplementación del controlador:

public class HomeController : Controller
{
    private readonly IMyService _myService;
    private readonly IMyLogic _myLogic;

    #region CTOR
    public HomeController(IMyService myService, IMyLogic myLogic)
    {
        _myService = myService;
        _myLogic = myLogic;
    }
    #endregion

    public ActionResult Index()
    {
        // Obtaining string directly from the IMyService
        var sService = _myService.GetMyStringFromDLL();

        // Obtaining string through the IMyLogic
        var sLogic = _myLogic.GetMyString();

        return View(new List<string>() { sService, sLogic} );
    }
}

Y finalmente, cuando se Homeejecutó el método de acción predeterminado del controlador, se muestran las siguientes dos líneas:

MyService.GetMyStringFromDLL() is called.
MyLogic.GetMyString() -> MyService.GetMyStringFromDLL() is called.
1 MarkRabjohn Dec 21 2020 at 21:45

No he trabajado con Unity desde la última vez que trabajé en NopCommerce V1.90, sin embargo, recuerdo que todo lo que registre en su contenedor regresará si usa Resolve en una instancia que implementa IUnityResolver.

Básicamente, registró IMyService, pero también tendría que registrar IMyLogic; entonces, en lugar de hacer "var logic = new MyLogic ();", haría "var logic = resolver.Resolve (typeof (IMyLogic)); " y luego sus parámetros inyectados se resolverán de acuerdo con el inyector de dependencia (o obtendría los errores adecuados si faltaran).