1
votes

I have set up Castle Windsor IoC in MVC. I've read through the tutorials, and have done this:

public class HomeController : Controller
{

    /////////////////////////////////////////////////////////////
    // Attributes

    public ISearchService SearchService { get; set; }

    public IFeedbackService FeedbackService { get; set; }

The HomeController is responsible for handling searches and storing feedback, so the HomeController has two service references which are created by the IoC.

This isn't really what I want. I don't want to have to construct two service for each request on the Home controller, when most of the time they will not be needed. I want to construct them in the action function, when I know for sure they will be needed.

Previously, I used Microsoft Unity and there was a lot of ServiceLocator.Resolve<thing>() in my action functions. This is a real pain when it comes to testing, and I have read it is unadvisable (and anti-pattern).

My wish is for this:

[HttpPost]
public ActionResult(FeedbackViewModel vm,IFeedbackService feedbackService)
{

and have the IoC construct the second parameter but not the first. Is that possible?

If not, I will have to find a way to create the members of the service constructor without using a static reference.

Thanks for reading.

Update using Brent Mannering's answer:

I tried creating a ResolveThis parameter but it is impossible to check for the attribute in the BindModel function. So I have stuck with resolving all interfaces:

/// <summary>
/// 
/// </summary>
public class DependencyModelBinder : DefaultModelBinder
{

    /////////////////////////////////////////////////////////////
    // Attributes

    private IKernel _kernel;


    /////////////////////////////////////////////////////////////
    // Construction

    public DependencyModelBinder(IKernel kernel)
    {

        // Initialize member variables
        _kernel = kernel;

    }


    /////////////////////////////////////////////////////////////
    // Implementation

    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {

        // If this is an interface...
        if(bindingContext.ModelType.IsInterface)
        {

            // Resolve the object and return 
            return _kernel.Resolve(bindingContext.ModelType);

        }

        // Call base-class function
        return base.BindModel(controllerContext, bindingContext);

    }


}
1
I recommend you just you plain old DI and inject the services into the constructor of the controller.Maess
Thanks Maess, but that means constructing two services, and all the repository classes that go with them, when they probably wont be used. Is this not shamefully inefficient?BrainProxy
Maess is right. You should use constructor injection, because properties are optional in Castle Windsor. If you forget to register one of those dependencies, the controller would still be able to be created, but the property will be null. This will cause a NullReferenceException later on. It's best to have the application fail a.s.a.p. which means you should use constructor injection.Steven
Besides, have you tested what the performance difference is? Constructing a dependency graph is usually very fast (even with a framework like Castle), especially compared to the overhead a web framework has. It shouldn't make any noticeable difference if you inject dependencies that are only used part of the time. If one of those dependencies takes a long time to create, it means that the constructor of that dependency does too much and this class should be refactored.Steven

1 Answers

1
votes

Firstly, seems to me that you are violating the single responsibility principle, hence why you are running into this issue in the first place. You should probably look refactoring your controllers and move either the search or feedback actions (in fact probably both) into their own controller(s), and inject the service dependency in the constructor.

BUT, to answer your question directly. You could manage this with a custom model binder. Something along the lines of:

public class DependencyModelBinder : DefaultModelBinder
{
    public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        if (bindingContext.ModelType.IsInterface)
        {
            bindingContext.Model = // ServiceLocator logic here using bindingContext.ModelType
        }

        return base.BindModel(controllerContext, bindingContext);
    }
}

Then in your Application_Start add the following line:

ModelBinders.Binders.DefaultBinder = new DependencyModelBinder();