非コントローラークラスでUnityMVC 4を使用して依存性注入(DI)を追加する方法

Dec 18 2020

だから私は依存性注入を達成するためにUnityMVC-4を使用していて、それは私のControllerクラスでうまく機能しますが、非コントローラークラスに注入しようとするとすぐに取得NullReferenceExceptionし、注入されたオブジェクトがフレームワークによって初期化されていないことがわかります。私が使用している対応するクラスを提供します。

Controller クラス(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;
    }
}

また、コントローラー以外の方法で同じことを行うと(DIはここでは機能しません)、次のようになります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.csクラスは次のようになります。

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

    }

}

上記の行に表示されているcontainer.RegisterType<IMyService , MyService>();場合、インターフェイスとその具体的な実装は別のモジュールにあります。

そして私Global.asax.csは:

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

どうすればクラスIMyService内に注入できMyLogicますか?

回答

4 Jackdaw Dec 21 2020 at 20:20

属性[InjectionConstructor]を使用して、MyLogicクラスがコンストラクターに注入されるオブジェクトに依存していることをUnityに通知します。

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

実際に[InjectionConstructor]は、注入されたクラスに複数のコンストラクターが含まれている場合に使用することをお勧めします。したがって、属性は明確化を解決するために使用されます。Unity質問コードにコードのすべての部分が含まれていないために、が必要な型を解決できないのは私の仮説でした。ただし、以下のテストコードでは、[InjectionConstructor]コンストラクターが1つしか宣言されていないため、属性は不要です。

これがテストコードです。

IMyServiceインタフェース定義:

public interface IMyService
{
    string GetMyStringFromDLL();
}

ILogicインタフェース定義:

public interface IMyLogic
{
    string GetMyString();
}

MyLogic実装:

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

MyService実装:

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

Bootstrapper初期設定:

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

Homeコントローラの実装:

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

そして最後に、Homeコントローラーのデフォルトのアクションメソッドが実行されると、次の2行が表示されます。

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

前回NopCommerceV1.90で作業を行って以来、Unityを使用していませんが、IUnityResolverを実装するインスタンスでResolveを使用すると、コンテナーに登録したものがすべて戻ってくることを覚えています。

つまり、基本的にはIMyServiceを登録しましたが、IMyLogicも登録する必要があります。「varlogic = new MyLogic();」を実行する代わりに、「var logic = resolver.Resolve(typeof(IMyLogic));」を実行します。 「」次に、注入されたパラメーターは依存性注入に従って解決されます(または、パラメーターが欠落している場合は適切なエラーが発生します)。