Search code examples
c#dependency-injectionextension-methodsservice-locator

How to avoid service locator in .net extension methods


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.


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