Comment ajouter une injection de dépendances (DI) à l'aide de Unity MVC 4 dans une classe sans contrôleur

Dec 18 2020

J'utilise donc Unity MVC-4 pour réaliser l'injection de dépendances et cela fonctionne très bien avec mes Controllerclasses, mais dès que j'essaye d'injecter dans ma classe non contrôleur, j'obtiens le NullReferenceExceptionet je peux voir que mes objets injectés ne sont pas initialisés par le framework . Je vais vous donner les classes correspondantes que j'utilise:

Controller classe (DI fonctionne):

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

Et si je fais la même chose dans une méthode non contrôleur (DI ne fonctionne pas ici), j'obtiens 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;
    }
}

Ma BootStrapper.csclasse ressemble à:

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 vous voyez ci-dessus dans la ligne container.RegisterType<IMyService , MyService>();, l'interface et sa mise en œuvre concrète se trouvent dans un module séparé.

Et mon Global.asax.csest:

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

Comment puis-je injecter le IMyServiceen MyLogicclasse?

Réponses

4 Jackdaw Dec 21 2020 at 20:20

Utilisez l'attribut [InjectionConstructor] pour indiquer à l'Unity que la MyLogicclasse dépend d'un objet qui doit être injecté dans le constructeur:

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

En fait, il [InjectionConstructor]est recommandé d'utiliser lorsque la classe injectée contient plusieurs constructeurs. Par conséquent, l'attribut est utilisé pour résoudre la désambiguïsation. C'était juste mon hypothèse pour laquelle le Unityne peut pas résoudre le type requis, car le code de la question ne contient pas toutes les parties du code. Mais dans le code de test ci-dessous, l' [InjectionConstructor]attribut n'est pas nécessaire, car un seul constructeur est déclaré.

Voici le code de test.

La IMyServicedéfinition de l'interface:

public interface IMyService
{
    string GetMyStringFromDLL();
}

La ILogicdéfinition de l'interface:

public interface IMyLogic
{
    string GetMyString();
}

La MyLogicmise en œuvre:

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 MyServicemise en œuvre:

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

L' Bootstrapperinitialisation:

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

L' Homeimplémentation du contrôleur:

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

Et enfin, lorsque la méthode d'action par défaut du Homecontrôleur est exécutée, les deux lignes suivantes s'affichent:

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

Je n'ai pas travaillé avec Unity depuis la dernière fois que j'ai travaillé sur NopCommerce V1.90, mais je me souviens que tout ce que vous enregistrez dans votre conteneur revient si vous utilisez Resolve sur une instance implémentant IUnityResolver.

Donc, fondamentalement, vous avez enregistré IMyService, mais vous devez également enregistrer IMyLogic - alors au lieu de faire "var logic = new MyLogic ();", vous feriez "var logic = resolver.Resolve (typeof (IMyLogic)); " puis vos paramètres injectés seront résolus en fonction de l'injecteur de dépendances (ou vous obtiendriez les erreurs appropriées si elles étaient manquantes).