Como adicionar injeção de dependência (DI) usando Unity MVC 4 em uma classe não controladora

Dec 18 2020

Portanto, estou usando Unity MVC-4 para obter injeção de dependência e funciona muito bem com minhas Controllerclasses, mas assim que tento injetar em minha classe não controladora, recebo o NullReferenceExceptione posso ver que meus objetos injetados não são inicializados pelo framework . Vou lhe dar as classes correspondentes que estou usando:

Controller classe (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;
    }
}

E se eu fizer a mesma coisa em um método sem controlador (DI não funciona aqui), recebo 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;
    }
}

Minha BootStrapper.csclasse se parece com:

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)
    {

    }

}

Se você vir acima na linha container.RegisterType<IMyService , MyService>();, a interface e sua implementação concreta estão em um módulo separado.

E o meu Global.asax.csé:

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

Como posso injetar o IMyServiceem MyLogicclasse?

Respostas

4 Jackdaw Dec 21 2020 at 20:20

Use o atributo [InjectionConstructor] para informar ao Unity que a MyLogicclasse depende de um objeto que deve ser injetado no construtor:

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

Na verdade, [InjectionConstructor]é recomendado o uso quando a classe injetada contém mais de um construtor. Portanto, o atributo é usado para resolver a desambiguação. Foi apenas minha hipótese por que o Unitynão pode resolver o tipo necessário, porque o código da pergunta não contém toda a parte do código. Mas no código de teste abaixo o [InjectionConstructor]atributo não é necessário, porque apenas um construtor é declarado.

Aqui está o código de teste.

A IMyServicedefinição da interface:

public interface IMyService
{
    string GetMyStringFromDLL();
}

A ILogicdefinição da interface:

public interface IMyLogic
{
    string GetMyString();
}

A MyLogicimplementação:

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;
    }
}

A MyServiceimplementação:

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

A Bootstrapperinicialização:

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;
    }
}

A Homeimplementação do 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} );
    }
}

E, finalmente, quando o método de ação padrão do Homecontrolador é executado, as duas linhas a seguir são exibidas:

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

Não trabalho com o Unity desde a última vez em que fiz algum trabalho no NopCommerce V1.90, no entanto, lembro que tudo o que você registrar em seu contêiner retornará se você usar Resolve em uma instância que implementa IUnityResolver.

Então, basicamente, você registrou IMyService, mas também teria que registrar IMyLogic - então, em vez de fazer "var logic = new MyLogic ();", você faria "var logic = resolver.Resolve (typeof (IMyLogic)); " e então seus parâmetros injetados serão resolvidos de acordo com o injetor de dependência (ou você obteria os erros apropriados se eles estivessem ausentes).