ServiceLocator Shim For .NET Core

I want to start off this post by saying if you are starting a new .NET Core project and you are looking to use a ServiceLocator. Don’t. There are numerous posts out there discussing how using a ServiceLocator is an “anti-pattern” and what not, and frankly I find anything that uses a ServiceLocator a right pain in the ass to test. Realistically in a brand new .NET Core project, you have dependency injection out of the box without the need to use any sort of static ServiceLocator. It’s simply not needed.

But, if you are trying to port across some existing code that already uses a ServiceLocator, it may not be as easy to wave a magic wand across it all and make everything work within the confines of .NET Core’s dependency injection model. And for that, we will have to work out a new way to “shim” the ServiceLocator in.

An important thing to note is that the “existing” code I refer to in this post is the “ServiceLocator” class inside the “Microsoft.Practices” library. Which itself is also part of the “Enterprise Library”. It’s a little confusing because this library is then dragged along with DI frameworks like Unity back in the day, so it’s hard to pinpoint exactly what ServiceLocator you are using. The easiest way is, are you calling something that looks like this :

ServiceLocator.Current.GetInstance<IMyService>();

If the answer is yes, then you are 99% likely using the Microsoft.Practices ServiceLocator. If you are using a different service locator but it’s still a static class, you can probably still follow along but change the method signature to your needs.

Creating Our Service Locator Shim

The first thing we are going to do is create a class that simply matches our existing ServiceLocator structure and method signatures. We want to create it so it’s essentially a drop in for our existing ServiceLocator so all the method names and properties should match up perfectly. The class looks like :

public class ServiceLocator
{
    private ServiceProvider _currentServiceProvider;
    private static ServiceProvider _serviceProvider;

    public ServiceLocator(ServiceProvider currentServiceProvider)
    {
        _currentServiceProvider = currentServiceProvider;
    }

    public static ServiceLocator Current
    {
        get
        {
            return new ServiceLocator(_serviceProvider);
        }
    }

    public static void SetLocatorProvider(ServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public object GetInstance(Type serviceType)
    {
        return _currentServiceProvider.GetService(serviceType);
    }

    public TService GetInstance<TService>()
    {
        return _currentServiceProvider.GetService<TService>();
    }
}

It can be a bit confusing because we are mixing in static methods with instance ones. But let’s walk through it.

On the static end, we only have one method that is SetLocatorProvider , this allows us to pass in a ServiceProvider  instance that will be used for all service location requests. ServiceProvider is the built in DI that comes with .NET Core (We’ll take a look at how we hook it up in a second). We also have a static property called Current  that simply creates an actual instance of ServiceLocator, providing us with access to the “instance” methods.

Once we have an instance of the ServiceLocator class, we then gain access to the GetInstance  methods, which perfectly match the existing ones of the old ServiceLocator class. Awesome!

Wiring It Up To .NET Core Service Provider

The next part is easy! In our ConfigureServices method of our startup.cs. We need to set the LocatorProvider. It looks like so :

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    ServiceLocator.SetLocatorProvider(services.BuildServiceProvider());
}

So all we are doing is passing in an instance of our ServiceProvider and this will be used to fetch any instances that are required going forward.

This is actually all we need to do. If you have existing code that utilizes the ServiceLocator, barring a change to any “Using” statements to be swapped over, you should actually be all ready to go!

Testing It Out

Let’s give things a quick test to make sure it’s all working as intended.

I’m going to create a simple test class with a matching interface.

public interface IMyService
{
    string HelloWorld();
}

public class MyService : IMyService
{
    public string HelloWorld()
    {
        return "HelloWorld";
    }
}

I need to wire this up in my ConfigureServices method of startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<IMyService, MyService>();

    services.AddMvc();
    ServiceLocator.SetLocatorProvider(services.BuildServiceProvider());
}

Then I’m just going to test everything on a simple API endpoint in my .NET Core web app.

[HttpGet]
public string Get()
{
    IMyService myService = ServiceLocator.Current.GetInstance<IMyService>();
    return myService.HelloWorld();
}

Give it a run and…

All up and running!

6 thoughts on “ServiceLocator Shim For .NET Core”

  1. I’m able to use MvvmLight replacing the simpleIoc with MS out of the box DI in my .net core 3 WFP app with your code example.

    Thank you so much,
    KC

    Reply
  2. Calling services.BuildServiceProvider in ConfigureServices will create a copy of the service provider.
    An alternative is to set the the locator provider after the service provider has been built. E.g. in the “Main” method:
    var host = CreateWebHostBuilder(args).Build();
    // Initialize ServiceLocator for legacy code.
    ServiceLocator.SetLocatorProvider(host.Services);
    host.Run();

    Reply

Leave a Comment