Cara menambahkan Dependency Injection (DI) menggunakan Unity MVC 4 di Non Controller Class

Dec 18 2020

Jadi saya menggunakan Unity MVC-4 untuk mencapai Dependency Injection dan berfungsi dengan baik dengan Controllerkelas saya tetapi segera setelah saya mencoba untuk menyuntikkan di kelas non-controller saya, saya mendapatkan NullReferenceExceptiondan saya dapat melihat bahwa objek yang saya masukkan tidak diinisialisasi oleh kerangka kerja. . Saya akan memberi Anda kelas yang sesuai yang saya gunakan:

Controller class (DI bekerja):

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

Dan jika saya melakukan hal yang sama dalam metode non-pengontrol (DI tidak berfungsi di sini), saya mendapatkan 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;
    }
}

BootStrapper.csKelas saya terlihat seperti:

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

    }

}

Jika Anda melihat baris di atas container.RegisterType<IMyService , MyService>();, antarmuka dan implementasi konkretnya berada dalam modul terpisah.

Dan saya Global.asax.csadalah:

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

Bagaimana saya bisa menyuntikkan IMyServicedi MyLogickelas?

Jawaban

4 Jackdaw Dec 21 2020 at 20:20

Gunakan atribut [InjectionConstructor] untuk memberi tahu Unity bahwa MyLogickelas tersebut bergantung pada objek yang akan dimasukkan ke dalam konstruktor:

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

Sebenarnya [InjectionConstructor]direkomendasikan untuk digunakan ketika kelas yang diinjeksi berisi lebih dari satu konstruktor. Oleh karena itu, atribut digunakan untuk menyelesaikan disambiguasi. Itu hanya hipotesis saya mengapa Unitytidak dapat menyelesaikan jenis yang diperlukan, karena kode pertanyaan tidak mengandung semua bagian kode. Tetapi dalam kode uji di bawah [InjectionConstructor]atribut tidak diperlukan, karena hanya satu konstruktor yang dideklarasikan.

Ini dia kode tesnya.

The IMyServicedefinisi antarmuka:

public interface IMyService
{
    string GetMyStringFromDLL();
}

The ILogicdefinisi antarmuka:

public interface IMyLogic
{
    string GetMyString();
}

The MyLogicimplementasi:

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

The MyServiceimplementasi:

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

The Bootstrapperinisialisasi:

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

The Homekontroler implementasi:

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

Dan akhirnya ketika metode tindakan default dari Homepengontrol dijalankan, dua baris berikut ditampilkan:

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

Saya belum pernah bekerja dengan Unity sejak terakhir kali saya melakukan beberapa pekerjaan di NopCommerce V1.90, namun saya ingat bahwa apa pun yang Anda daftarkan di penampung akan kembali jika Anda menggunakan Resolve pada contoh yang mengimplementasikan IUnityResolver.

Jadi pada dasarnya, Anda mendaftarkan IMyService, tetapi Anda juga harus mendaftarkan IMyLogic - lalu alih-alih melakukan "var logic = new MyLogic ();", Anda akan melakukan "var logic = resolver.Resolve (typeof (IMyLogic)); " dan kemudian parameter yang Anda masukkan akan diselesaikan sesuai dengan injektor ketergantungan (atau Anda akan mendapatkan kesalahan yang tepat jika tidak ada).