Question

I'm looking for a clean pattern to use dependencies in .Net extension methods without explicitly newing-up or using a service locator:

public static class HttpContextExtensions
{
    public static SomeClass ExtensionMethod(this HttpContext context)
    {
        //looking to avoid this
        var dependency = ServiceLocator.GetService<DependencyType>();
        return dependency.DoSomething(context);
    }
}

Am I barking up the wrong tree here? Should I be looking for a more direct solution that passes context into a method? I'd like to continue using an extension if possible.

Was it helpful?

Solution

In the book "Dependency Injection in .NET" by Mark Seemann, in chapter 2 he talks about 4 different patterns of injection:

  1. Constructor Injection
  2. Property Injection
  3. Method Injection
  4. Ambient Context

The 4th one, Ambient Context, is a static property, which can be of an abstract type. This property can be set in the DI Root, thread context, call context, request context, etc. .NET Security, Transactions and other stuff like that use this pattern.

Here are links that will give you more details:

Here is some sample code:

public interface IOutput
{
    void Print(Person person);
}

public class ConsoleOutput : IOutput
{
    public void Print(Person person)
    {
        Console.WriteLine("{0} {1}", person.FirstName, person.LastName);
    }
}

public class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

public static class SampleContext
{
    public static IOutput Output { get; set; }
}

public static class ExtensionMethods
{
    public static void Print(this Person person)
    {
        SampleContext.Output.Print(person);
    }
}

static class Program
{
    static void Main()
    {
        //You would use your DI framework here
        SampleContext.Output = new ConsoleOutput();

        //Then call the extension method
        var person = new Person()
        {
            FirstName = "Louis-Pierre",
            LastName = "Beaumont"
        };

        person.Print();
    }
}

OTHER TIPS

You can't use extension methods for this. Extension methods are static and this means you can't use constructor injection. Only method injection is an option, but this means you have to pass in the dependencies as method arguments and this usually sucks because dependencies should usually be an implementation detail, but method injection makes the dependencies part of the contract, which means the consumer of the extension method should know about these dependencies (and get them injected).

So the solution is: don't use extension methods for anything that has dependencies on its own: write a proper class and abstraction for this.

A possible solution would be to instead make the extension method extend the class you're trying to inject and reference that dependency in an upstream context, such as a controller action, or any other non-static entry point that leads to this call.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top