Hinzufügen von Dependency Injection (DI) mit Unity MVC 4 in einer Nicht-Controller-Klasse

Dec 18 2020

Ich verwende Unity MVC-4, um Dependency Injection zu erreichen, und es funktioniert hervorragend mit meinen ControllerKlassen. Sobald ich jedoch versuche, in meine Nicht-Controller-Klasse zu injizieren, erhalte ich das NullReferenceExceptionund kann sehen, dass meine injizierten Objekte nicht vom Framework initialisiert werden . Ich werde Ihnen die entsprechenden Klassen geben, die ich benutze:

Controller Klasse (DI funktioniert):

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

Und wenn ich dasselbe in einer Nicht-Controller-Methode mache (DI funktioniert hier nicht), bekomme ich Folgendes 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;
    }
}

Meine BootStrapper.csKlasse sieht aus wie:

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

    }

}

Wenn Sie oben in der Zeile sehen container.RegisterType<IMyService , MyService>();, befindet sich die Schnittstelle und ihre konkrete Implementierung in einem separaten Modul.

Und mein Global.asax.csist:

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

Wie kann ich das IMyServicein der MyLogicKlasse injizieren ?

Antworten

4 Jackdaw Dec 21 2020 at 20:20

Verwenden Sie das Attribut [InjectionConstructor] , um der Unity mitzuteilen, dass die MyLogicKlasse von einem Objekt abhängig ist, das in den Konstruktor eingefügt werden soll:

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

Tatsächlich [InjectionConstructor]wird empfohlen, das zu verwenden, wenn die injizierte Klasse mehr als einen Konstruktor enthält. Daher wird das Attribut verwendet, um die Begriffsklärung aufzulösen. Es war nur meine Hypothese, warum der Unityerforderliche Typ nicht aufgelöst werden kann, da der Fragencode nicht den gesamten Teil des Codes enthält. Im folgenden Testcode wird das [InjectionConstructor]Attribut jedoch nicht benötigt, da nur ein Konstruktor deklariert ist.

Hier ist es der Testcode.

Die IMyServiceSchnittstellendefinition:

public interface IMyService
{
    string GetMyStringFromDLL();
}

Die ILogicSchnittstellendefinition:

public interface IMyLogic
{
    string GetMyString();
}

Die MyLogicImplementierung:

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

Die MyServiceImplementierung:

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

Die BootstrapperInitialisierung:

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

Die HomeController-Implementierung:

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

Und schließlich werden bei der HomeAusführung der Standardaktionsmethode des Controllers die folgenden zwei Zeilen angezeigt:

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

Ich habe seit meiner letzten Arbeit an NopCommerce V1.90 nicht mehr mit Unity gearbeitet. Ich erinnere mich jedoch, dass alles, was Sie in Ihrem Container registrieren, zurückkommt, wenn Sie Resolve für eine Instanz verwenden, die IUnityResolver implementiert.

Im Grunde haben Sie IMyService registriert, aber Sie müssten auch IMyLogic registrieren - anstatt "var logic = new MyLogic ();" auszuführen, würden Sie "var logic = resolver.Resolve (typeof (IMyLogic)) ausführen. "" und dann werden Ihre injizierten Parameter entsprechend dem Abhängigkeitsinjektor aufgelöst (oder Sie würden die richtigen Fehler erhalten, wenn sie fehlen würden).