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()
returnsnull
if a service does not exist,GetRequiredService()
throws an exception instead. If you're using a third-party container, useGetRequiredService
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 typeserviceType
. -or-null
if there is no service object of typeserviceType
.
Contrast that to the documentation for GetRequiredService()
:
GetRequiredService()
returns a service object of typeserviceType
. Throws anInvalidOperationException
if there is no service of typeserviceType
.
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
- returnsnull
if the service is not registeredGetRequiredService
- throws anException
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 implementGetRequiredService()
.
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 useGetService()
instead, then you ned to check fornull
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 usingGetService()
then you could end up with aNullReferenceException
some time later. Figuring out what caused the exception is always going to be more work than having anInvalidOperationException
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 returningnull
(withGetService()
) gives you no further insight. This was the main reason for the introduction ofGetRequiredService()
.
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 usingGetRequiredService()
. However, I'd argue the first two advantages still stand, and makeGetRequiredService
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 useGetService()
. However, I've also seen it used where a fallback service is used ifGetService()
returnsnull
. 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.