1

I'm using CastleWindsor as my dependency injection framework and it all works well when you're in a Controller, because we can make use of constructor injection with the controllerfactory.

But there are some specific cases that dependency injection ( constructor injection ) would not work.. For example: I want to be able to resolve my dependencies still with IOC in some utility classes or extension methods ( eg. of HtmlHelper ) in my Views. I know that some people wouldn't agree on that and rather keep the views dumb, but let's keep that out the discussion.

So this basically leaves me with one option and that's using... the service locator. So I know that the Service Locator is considered by most people as an anti pattern and I do understand why.. But how do you resolve your dependencies with IOC if you can't use dependency injection? For all that I know is that it's still better to use the service locator for IOC than having nothing. I would like to avoid the service locator pattern, but I do not seem to understand how to avoid it in some specific cases.

Next question is.. so even if you like/dislike the service locator. Which is the best option to implement this with CastleWindsor?

So I guess the options would be:

  1. Expose the container as a global object ( or through some other global object that wraps the container ) that you can retrieve from anywhere in your code. You can then just call the resolve and release methods on the container. One thing I don't like about this is, that we have to call release explicitly for transient lifestyle objects. If an inexperience developer does not do this, you'd end up having a memory leak.

  2. I also found: https://www.nuget.org/packages/CommonServiceLocator.WindsorAdapter and it has a lot of downloads.. ( Same principal as option 1 but more generic and wraps the container so you can swap the DI framework easily ) I looked into the code and I found out that the adapter only has methods to resolve objects. So I kinda wondered why there's no release method.. Does this mean this package has memory leak problems with transient lifestyle objects?

Hope somebody can give me some advice on these matters!

NightOwl888
  • 55,572
  • 24
  • 139
  • 212
qkp
  • 161
  • 1
  • 1
  • 12
  • 1
    Is it an option to convert your utility classes (I am assuming they are static classes or contain static methods) and your extension method into non-static classes and have them declare their dependencies in the constructor, and then make them implement some interfaces and inject them into the classes (I think controllers) that use them? I am saying this because usually anything that requires dependencies shouldn't be a static method. – Yacoub Massad Oct 03 '15 at 21:55

1 Answers1

5

How to avoid using service locator in static extension methods (HTML Helpers)?

First of all, avoid using static extension methods when possible. Static extension methods work well when you have small pieces of logic that are unlikely to change that have no dependencies (other than the class/interface they apply to). Of course, this isn't always possible, but it does cut down on this scenario quite a bit if you can use another approach instead.

HTML Helpers

2017/01/30 UPDATE: A better approach for using DI with HTML helpers is to use an abstract factory as in this answer, which allows for DI container lifetime management of the HTML helper dependencies.

When you do need to use a static method, one approach for injecting dependencies is to use property injection.

public static class MyHtmlHelperExtensions
{
    private static IHtmlHelperService htmlHelperService;

    // Property for use with dependency injection in the composition root
    public static IHtmlHelperService HtmlHelperService
    {
        set
        {
            if (value == null)
                throw new ArgumentNullException("value");
            if (htmlHelperService != null)
                throw new ArgumentExeption("HtmlHelperService cannot be set twice");
            htmlHelperService = value;
        }
    }

    // The static method simply calls the instance method of our service,
    // but does not contain any logic.
    public static MvcHtmlString MyHtmlHelper(this HtmlHelper htmlHelper)
    {
        return htmlHelperService.MyHtmlHelper(htmlHelper);
    }
}

Basically, the static HTML helper is just a facade, which delegates its methods to an Aggregate Service that contains the real services that do the actual work.

The HtmlHelperService is injected at application startup just after the DI container is built, but still inside of the composition root of the application.

// DIConfig.Register() will create the container and register all of our type mappings.
var container = DIConfig.Register();

// While we are still in the composition root, we instantiate and 
// assign our HtmlHelperService along with its dependency graph.
MyHtmlHelperExtensions.HtmlHelperService = container.Resolve<IHtmlHelperService>();

NOTE: In MVC6, there will be view components, which act more like controllers than static HTML helpers, to eliminate the need to create static HTML helpers with dependencies.

Attributes

As for Attributes, which are another common source of dependency injection problems that lead to the service locator, the best approach is to define the attribute with no behavior which can be done by splitting ActionFilterAttribute derived types into a "dumb" attribute and a DI-Friendly global action filter that is resolved in the composition root. See this MVC IActionFilter example and this MVC AuthorizeAttribute example.

There is no reason at all that an attribute needs to define behavior, since attributes only define metadata that can be explored by another service (which does have behavior). Therefore, an attribute never requires dependencies, only services that contain behavior do.

NightOwl888
  • 55,572
  • 24
  • 139
  • 212