5

So I am using Unity MVC-4 for achieving Dependency Injection and it works great with my Controller classes but as soon as I try to inject in my non controller class, I get the NullReferenceException and I can see that my injected objects are not initialized by the framework. I will give you the corresponding classes that I am using:

Controller class (DI works):

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

And if I do the same thing in a non controller method (DI does not work here), I get a 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;
    }
}

My BootStrapper.cs class looks like:

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

    }

}

If you see above in the line container.RegisterType<IMyService , MyService>();, the interface and its concrete implementation is in a separate module.

And my Global.asax.cs is:

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

How can I inject the IMyService in MyLogic class?

12
  • Does this answer your question? Using Dependency Injection outside of a Controller's constructor Commented Dec 17, 2020 at 18:31
  • @Rahul Sharma: What is your Unity.Mvc4 runtime version? Can you expose your IMyService and MyService definitions, please. Because I put your code in my application (uses Framework v4.5 too) and it is working. Commented Dec 19, 2020 at 19:32
  • @Rahul Sharma: According to your problem description the _myService field in the MyLogic class is null (not initialized - put break-point in the debugger and check it). This is means somehow the MyLogic.GetMyString() method is called before the Unity performed registering the type MyLogic mapping with the container. So, first, there is some code that related to problem and it not exposed in this post. Second, try to move two lines container.RegisterType<IMyService,...>(); container.RegisterType<IMyLogic,...); to the Bootstrapper.BuildUnityContainer() method. Commented Dec 19, 2020 at 19:57
  • @Jackdaw As you can see in my Global.asax that Bootstrapper.Initialise(); is first called to ensure that the container is mapped correctly with my service(s). I tried your other suggestion also and moved my service to Bootstrapper.BuildUnityContainer() method but unfortunately the outcome is the same. I get the same error. How I can check the Unity.Mvc4 runtime version? Also exposing my IMyService would not have any effect since the error is generated because _myService object is being received as null. I have double checked MyService logic and everything looks right there. Commented Dec 19, 2020 at 20:16
  • 1
    How are you instantiating MyLogic? Also, unless you have specific need for the HierarchicalLifetimeManager, I'd recommend using TransientLifetimeManager, PerResolveLifetimeManager, or PerThreadLifetimeManager. With HierarchialLifetimeManager, the child container is not always the same as the parent container. tutorialsteacher.com/ioc/lifetime-manager-in-unity-container Commented Dec 26, 2020 at 3:53

2 Answers 2

6
+50

Use attribute [InjectionConstructor] to tell the Unity that the MyLogic class is dependent on an object which is to be injected in the constructor:

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

Actually the [InjectionConstructor] is recommended to use when the injected class contains more than one constructor. Therefore, the attribute is used to resolve the disambiguation. It was just my hypothesis why the Unity cannot resolve the required type, because of the question code does not contain all part of the code. But in the test code below the [InjectionConstructor] attribute not needed, because of only one constructor is declared.

Here it is the test code.

The IMyService interface definition:

public interface IMyService
{
    string GetMyStringFromDLL();
}

The ILogic interface definition:

public interface IMyLogic
{
    string GetMyString();
}

The MyLogic implementation:

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 MyService implementation:

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

The Bootstrapper initialization:

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 Home controller implementation:

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

And finally when default action method of the Home controller executed the following two lines are displayed:

MyService.GetMyStringFromDLL() is called.
MyLogic.GetMyString() -> MyService.GetMyStringFromDLL() is called.
Sign up to request clarification or add additional context in comments.

9 Comments

Thank you for the detailed answer and I really appreciate the effort but unfortunately it does not work in my case and maybe that could because of the version I am using for Unity or some other thing which I cannot pinpoint. I have tried all the combinations suggested by you and others and it simply does not work. I managed to get it resolved by adding this before I call GetMyStringFromDLL: var sService= DependencyResolver.Current.GetService<IMyService>();. I even tried the [InjectionConstructor] attribute since my class has more than one constructor but it still did not work.
Can you try the above exercise in a non controller class and see if it working ? Basically what I mean is that try to inject the _myService and then call var sService = _myService.GetMyStringFromDLL(); in a non controller class that does not inherit from Controller class.
@Rahul Sharma: But this is exactly that you have in the Logic class. The Logic class does not inherit from the Controller and it has var mystring = _myService.GetMyStringFromDLL(); call. Did I misunderstand something?
Ohh yes, you are right. I do strongly believe that your solution will suffice but in my case it is not working. I will go ahead and award the bounty to you. Thanks again. If you could tell me the version that you are using for Unity and MVC, that would be great.
@Rahul Sharma: My test environment is: target version .NET Framework 4.5, Unity 4.0.1. I'll be happy to help if you can modify your example to reproduce the problem.
|
1

I haven't worked with Unity since I last did some work on NopCommerce V1.90, however I do remember that whatever you register in your container does come back if you use Resolve on an instance implementing IUnityResolver.

So basically, you registered IMyService, but you'd also have to register IMyLogic - then instead of doing "var logic = new MyLogic();", you'd do "var logic = resolver.Resolve(typeof(IMyLogic));" and then your injected parameters will be resolved according to the dependency injector (or you'd get the proper errors if they were missing).

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.