1

We have an MVC3 controller in which there is some 'common' work that we rolled into the controller constructor. Some of that common work is done by a losely coupled class (say ourService) that's dynamically resolved through Unity (for IoC / Dependency injection). ourService is null (i.e. not resolved) in the Controller's constructor BUT it's properly resolved in the usual Controller methods. The simple demo code below shows the issue:

public class Testing123Controller : BaseController
{
    [Dependency]
    public IOurService ourService { get; set; }

    public Testing123Controller()
    {
            ourService.SomeWork(1); // ourService=null here !!
            ...
    }

    public ActionResult Index()
    {
            ourService.SomeWork(1); // resolved properly here here !!
            ...
    }
    ...
}

Question:

  1. Why is there this different in Unity resolution behavior? I would expect consistent behavior.
  2. How can I fix it so Unity resolves this even in the controller's contructor?

The way we've setup Unity 2.0 is :

Global.asax

Application_Start()
{
    ...
    Container = new UnityContainer();
    UnityBootstrapper.ConfigureContainer(Container);
    DependencyResolver.SetResolver(new UnityDependencyResolver(Container));
    ...
}

public static void ConfigureContainer(UnityContainer container)
{
    ...
    container.RegisterType<IOurService, OurService>();
    ...
}

IOurService.cs

public interface IOurService
{
    bool SomeWork(int anInt);
}

OurService.cs

public class OurService: IOurService
{
    public bool SomeWork(int anInt)
    {
        return ++anInt; //Whew! Time for a break ...
    }
}
1

3 Answers 3

5

As a basic principle of classes, before an instance property can be set, the instance has to be instantiated.

Unity needs to set the dependency property, but it can't do so until the instance has been fully instantiated - i.e. the constructor must have completed executing.

If you are referencing the dependency property in the constructor, then this is too early - there is no way for Unity to have set it yet - and therefore it will be unset (i.e. null).

If you need to use the dependency in the constructor, then you must use constructor injection. Although in general, using constructor injection is usually the better method anyway:

public class Testing123Controller : BaseController
{
    public IOurService ourService { get; set; }

    public Testing123Controller(IOurService ourService)
    {
            this.ourService = ourService;
            this.ourService.SomeWork(1); // ourService no longer null here
            ...
    }

    public ActionResult Index()
    {
            ourService.SomeWork(1); // also resolved properly here
            ...
    }
    ...
}

Note: In the example I left ourService as a publicly gettable & settable property in case other parts of your code need to access it. If on the other hand it is only accessed within the class (and had only been made public for Unity purposes), then with the introduction of constructor injection, it would be best to make it a private readonly field.

Sign up to request clarification or add additional context in comments.

4 Comments

Can you explain why it should be a field (private readonly IOurService ourService;) instead of a property (public IOurService ourService { get; })? Most documentation (MSDN, forums) suggest properties over fields. Agree with the concept that they should be isolated (the public with {get;set;} was another dev's code)
It really depends on usage. If it needs to be publicly accessed and publicly set, then use a public property with public 'get' and 'set' accessors. If it needs to be publicly accessed but only privately set, then use a public property with a public 'get', but a private 'set'. If it shouldn't be accessed publicly and needs to be manipulated within the class (i.e. set,read,updated) use a private (non-readonly) field. If it shouldn't be accessed publicly and only needs to be read privately (after an initial set), then use a private readonly field.
But Unity's [Dependency] decorations are not valid on fields, it needs to be a property, indexer or param. And if a property, then you must have {get;set;} so we can set it in the constructor of the same class - just per your answer. So I've changed it to private with {get;set;}
BTW, for the ourService that's passed to the controller constructor-I can't think of a class that's called prior to the controller's constructor (in terms of lifecycle). Even if there was one, how would that class actually resolve it? We have been offloading all resolving stuff to Unity...
1

You are using property injection (by using the Dependency attribute) rather than constructor injection so the dependency is not resolved until after the controller is instantiated. If you want to have access to the dependency is the constructor, just add it to the ctor:

private readonly IOurService _ourService { get; set; }

public Testing123Controller(IOurService ourService)
{
  _ourService = ourService;
  _ourService.SomeWork(1); // ourService=null here !!
  ...
}

2 Comments

Who would set the ourService that's passed to the controller's constructor? AFAIK, the parameter-less Controller constructor is the 1st that's called in user code from the external MVC framework.
If your UnityDependencyResolver is working correctly (you could do a lot worse than using the Unity.Mvc3 NuGet package), then when the framework tries to instantiate the controller, it will determine that in order to instantiate it, an implementation of IOurService is required. If an implementation of IOurService is registered with Unity, it will instantiate it and then use it to instantiate the controller.
0

I'm afraid that for Unity to work on the constructor, the instance itself must be resolved using Unity. The fact that the Service property is null in the constructor supports this idea. If you call the Controller yourself (NOT with Unity), Unity has no time to resolve the property.

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.