Jak dodać iniekcję zależności (DI) przy użyciu Unity MVC 4 w klasie bez kontrolera

Dec 18 2020

Więc używam Unity MVC-4 do osiągnięcia Dependency Injection i działa świetnie z moimi Controllerklasami, ale gdy tylko spróbuję wstrzyknąć w mojej klasie niebędącej kontrolerem, otrzymuję NullReferenceExceptioni widzę, że moje wstrzyknięte obiekty nie są inicjowane przez framework . Podam Ci odpowiednie klasy, których używam:

Controller klasa (prace DI):

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

A jeśli zrobię to samo w metodzie bez kontrolera (DI tutaj nie działa), otrzymuję 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;
    }
}

Moja BootStrapper.csklasa wygląda tak:

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

    }

}

Jeśli widzisz powyżej w wierszu container.RegisterType<IMyService , MyService>();, interfejs i jego konkretna implementacja znajdują się w osobnym module.

A moje Global.asax.csjest:

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

Jak mogę wstrzyknąć IMyServicew MyLogicklasie?

Odpowiedzi

4 Jackdaw Dec 21 2020 at 20:20

Użyj atrybutu [InjectionConstructor], aby poinformować Unity, że MyLogicklasa jest zależna od obiektu, który ma zostać wstrzyknięty w konstruktorze:

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

W rzeczywistości [InjectionConstructor]zalecane jest użycie, gdy wstrzyknięta klasa zawiera więcej niż jeden konstruktor. Dlatego atrybut jest używany do rozwiązania ujednoznacznienia. To była tylko moja hipoteza, dlaczego Unitynie można rozwiązać wymaganego typu, ponieważ kod pytania nie zawiera całej części kodu. Ale w poniższym kodzie testu [InjectionConstructor]atrybut nie jest potrzebny, ponieważ zadeklarowano tylko jeden konstruktor.

Oto kod testowy.

IMyServiceDefinicja interfejsu:

public interface IMyService
{
    string GetMyStringFromDLL();
}

ILogicDefinicja interfejsu:

public interface IMyLogic
{
    string GetMyString();
}

MyLogicRealizacja:

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

MyServiceRealizacja:

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

BootstrapperInicjalizacji:

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

HomeRealizacja kontroler:

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

I na koniec, gdy domyślna metoda akcji Homewykonanej przez kontroler zostanie wyświetlona, ​​zostaną wyświetlone następujące dwie linie:

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

Nie pracowałem z Unity, odkąd ostatnio pracowałem nad NopCommerce V1.90, jednak pamiętam, że cokolwiek zarejestrujesz w swoim kontenerze, wróci, jeśli użyjesz Resolve na instancji implementującej IUnityResolver.

Więc w zasadzie zarejestrowałeś IMyService, ale musiałbyś również zarejestrować IMyLogic - wtedy zamiast robić "var logic = new MyLogic ();", zrobiłbyś "var logic = resolver.Resolve (typeof (IMyLogic)); " a wtedy wprowadzone parametry zostaną rozwiązane zgodnie z wtryskiwaczem zależności (lub otrzymasz odpowiednie błędy, jeśli ich brakowało).