Quantcast
Channel: Andrew Lock | .NET Escapades
Viewing all articles
Browse latest Browse all 743

The difference between GetService() and GetRequiredService() in ASP.NET Core

$
0
0

This post looks at the GetService<T>() and GetRequiredService<T>() methods of the default/built-in ASP.NET Core DI container, provided in Microsoft.Extensions.DependencyInjection. I'll describe the differences between them and which one you should use.

tl;dr GetService() returns null if a service does not exist, GetRequiredService() throws an exception instead. If you're using a third-party container, use GetRequiredService where possible - in the event of an exception, the third party container may be able to provide diagnostics so you can work out why an expected service wasn't registered.

The heart of the container - the IServiceProvider interface

At the heart of the ASP.NET Core dependency injection abstraction is the IServiceProvider interface. This interface is actually part of the base class library, in the System namespace. The interface itself is very simple:

public interface IServiceProvider
{
    object GetService(Type serviceType);
}

Once you've registered all your classes with the DI container (using IServiceCollection), pretty much all a DI container needs to do is allow you to retrieve an instance of an object using GetService().

Of course, you typically shouldn't be using the IServiceProvider directly in your code at all. Instead, you should be using standard constructor injection, and letting the framework worry about using IServiceProvider behind the scenes.

Using the IServiceProvider directly is an example of the service locator pattern This is generally considered an anti-pattern, as it hides a class' dependencies.

However, there are some times when you don't have a choice. For example, if you're trying to inject services into an attribute, or use "forwarded" types when configuring the DI container, you'll need to use the IServiceProvider directly.

Comparing GetService<T>() and GetRequiredService<T>()

Seeing as we're not using .NET 1.0 anymore, if you want to retrieve a service from the IServiceProvider, you've probably used the generic GetService<T>() extension method, instead of the GetService(Type) interface method. But you may have also noticed the similar GetRequiredService<T>() extension method - the question is, what's the difference between them, and which should you use?

Before we look at any code, let's discuss the expected behaviour of the methods. First of all, from the documentation of the GetService() method:

GetService() returns a service object of type serviceType. -or- null if there is no service object of type serviceType.

Contrast that to the documentation for GetRequiredService():

GetRequiredService() returns a service object of type serviceType. Throws an InvalidOperationException if there is no service of type serviceType.

So both methods behave the same when an instance of the requested serviceType is available. The difference is in their behaviour when the serviceType has not been registered:

  • GetService - returns null if the service is not registered
  • GetRequiredService - throws an Exception if the service is not registered

Now we've cleared that up, lets look at some code.

The ServiceProviderServiceExtensions class in the Microsoft.Extensions.DependencyInjection.Abstractions library implements the generic version of both GetService<T>() and GetRequiredService<T>(), as shown below.

I've removed some of the precondition checks from the code in this post; if you want to see the full code, check it out on GitHub.

public static class ServiceProviderServiceExtensions
{
    public static T GetService<T>(this IServiceProvider provider)
    {
        return (T)provider.GetService(typeof(T));
    }

    public static T GetRequiredService<T>(this IServiceProvider provider)
    {
        return (T)provider.GetRequiredService(typeof(T));
    }
}

Both methods are virtually the same - the generic extension methods delegate to the non-generic versions of GetService() and GetRequiredService(). They're simply a convenience so you don't need to use the more wordy typeof() and typecast in your own code.

The non-generic version of GetService() is part of the IServiceProvider interface, but the non-generic GetRequiredService() implementation is an extension method in the same class:

public static class ServiceProviderServiceExtensions
{
    public static object GetRequiredService(this IServiceProvider provider, Type serviceType)
    {
        var requiredServiceSupportingProvider = provider as ISupportRequiredService;
        if (requiredServiceSupportingProvider != null)
        {
            return requiredServiceSupportingProvider.GetRequiredService(serviceType);
        }

        var service = provider.GetService(serviceType);
        if (service == null)
        {
            throw new InvalidOperationException(Resources.FormatNoServiceRegistered(serviceType));
        }

        return service;
    }
}

The first step in in the method is to check whether the provided IServiceProvider also implements ISupportRequiredService. This interface provides an underlying non-generic GetRequiredService implementation, so if the service provider implements it, GetRequiredService() is called directly.

The ASP.NET Core built-in DI container does not implement ISupportRequiredService - only third-party containers implement GetRequiredService().

If the IServiceProvider does not implement ISupportRequiredService, then the required exception-throwing behaviour is implemented as you might expect: GetService() is called, and an exception is thrown if it returns null.

So which method should you use?

As I said earlier, ideally, neither!

Using ISeviceProvider in your own code is typically a sign you're using the service locator anti-pattern, so it should generally be avoided. However, in cases where it's necessary due to design constraints (e.g. you can't use DI in an attribute), or as part of the DI container configuration itself, which should you use?

Based on the original issue in GitHub that requested adding GetRequiredService(), and the previous concerns raised by Jeremy D. Miller, I think the rule in pretty much all cases is:

Use GetRequiredService()

This has a number of advantages:

  • Reduces duplication. If a service is not available, with GetRequiredService() an exception is thrown immediately. If you use GetService() instead, then you ned to check for null in the calling code, and often will need to throw an exception anyway. That null-checking code will need to be duplicated everywhere.
  • Fails fast. If you forget to check for null when using GetService() then you could end up with a NullReferenceException some time later. Figuring out what caused the exception is always going to be more work than having an InvalidOperationException that explicitly tells you.
  • Allows advanced diagnostics for third-party containers. One of the big benefits of StructureMap and some other third-party containers are their ability to provide detailed exception messages as to why a service was not found. If you're using GetRequiredService(), the third-party container itself generates the exception, and so can provide additional container-specific information. Just returning null (with GetService()) gives you no further insight. This was the main reason for the introduction of GetRequiredService().

I've seen a couple of arguments against GetRequiredService(), but I don't think either of them hold up to scrutiny:

  • "I'm not using a third-party container". If you're using the built-in container (which doesn't implement ISupportRequiredService) then you won't benefit from any additional diagnostics by using GetRequiredService(). However, I'd argue the first two advantages still stand, and make GetRequiredService worth using. Plus if you add a third-party container later, you're already using the best practice.
  • "I have optional services, that are only sometimes registered with the DI container.". This is probably the only valid reason to use GetService(). If you have code that should only run if a given service is registered then you may need to use GetService(). However, I've also seen it used where a fallback service is used if GetService() returns null. In my opinion, this is rarely a good pattern for application code. Orchestration of fallbacks should be something that happens as part of your DI container configuration, not where the service is consumed.

So there you have it - GetService() vs. GetRequiredService(). Before I dug into it further, I was somewhat arbitrary in when I chose one over the other, but now I'll make sure I always use GetRequiredService() as a matter of course.

Summary

GetService() is the only method on IServiceProvider, the central interface in the ASP.NET Core DI abstractions. Third-party containers can also implement an optional interface ISupportRequiredService which provides the GetRequiredService() method. These methods behave the same when a requested serviceType is available. If the service is not available (i.e. it wasn't registered), GetService() returns null, whereas GetRequiredService() throws an InvalidOperationException.

The main benefit of GetRequiredService() over GetService() is that it allows third-party containers to provide additional diagnostic information when a requested service is not available. For that reason, it's always best to use GetRequiredService() when using a third-party container. Personally, I will use it everywhere, even if I'm only using the built-in DI container.


Viewing all articles
Browse latest Browse all 743

Trending Articles