Should I be Checking Injected Dependencies for Null?

In this post, I present my Top 5 scenarios of null injections from the .NET Dependency Injection container and use these to justify adding guard clauses to your C# constructors in consuming classes.

Background

I was recently listening to an episode of the Dot Net Core Show with guest Layla Porter (@LaylaCodesIt) on the topic of her brilliant talk TDD and the Terminator. (Go watch the talk and listen to the podcast!)

Around 43 minutes into the podcast, my ears pricked up when Layla mentioned a conversation that we had when she presented the talk at the Dot Net Oxford user group in the UK.

The conversation was instigated by a question (approximately 01:12:00 in the video) regarding whether your code should be checking constructor parameters for null given that the Microsoft Dependency Injection container will throw an InvalidOperationException if it cannot resolve the dependency.

In the podcast, Layla says that she includes null coalesce operations on incoming constructor parameters, primarily out of habit. She then goes on to discuss the arguments as to whether this is testing your code or the injection framework.

Jamie Taylor (host of the podcast) then rightly points out, what if someone instantiates your class directly and not via dependency injection – surely you should do the checks anyway to handle this situation?

This all got me thinking about whether there was anywhere that documents those scenarios when it is possible to receive a null from the container; either directly into a constructor parameter or indirectly, by not populating a class  member within an injected parameter instance.

A quick search on Google did not yield a definitive reference, so I decided I would write this blog post as a one stop shop to define my Top 5 list of when you should consider writing null checking guard clauses in your consuming class’ constructor.

“Unresolved Instances Throw an Exception so I Don’t Need a Null Check”

So let’s start with this statement. On initial consideration, the statement is correct, but there is some nuance to it that needs explaining.

If your consuming class requires an instance of a service type to be resolved by the container and the service type has not been registered, the container will throw an InvalidOperationException. The description within the exception provides details of the service type it was unable to resolve (either directly or indirectly due to a missing registration in the dependency chain).

A couple of other safeguards that are in place include:

  • Registering a singleton using an uninitialised variable/field or a variable/field/class member that results in a null at point of registration will trigger an ArgumentNullException at the registration, prior to the container being built
  • Registering an interface without an implementation will trigger an ArgumentException at the point the container is being built

However, there is a difference between not being able to resolve the service type due to a missing registration and the service type actually being resolved, but returning a null due to something happening within the registration or the instance construction process.

When a service resolves to a null, it will not throw an exception as null may conceptually be a valid result.

“The Way I Registered The Service Will Always Return an Instance”

That may be true at the time you write the registration code. If you are the sole maintainer and sole user of a project, then checking for nulls in the consuming classes may feel like overkill as it is potentially a lot of boilerplate code to write for what may appear to be little gain (though having many constructor parameters is a code smell that may need to be addressed anyway!)

However, source code changes over time or may “rot” so you should consider protecting your consuming class from your future self in case you change the service registration or some other aspect that may affect the dependency resolution.

Why Your Code May Change

If the software you are writing is not just for your own purposes, the potential for change happening in the future increases significantly if:

  • you are working in a team – another member of the team may deliberately change the registration for some reason or inadvertently do so through a source code merge that adds an additional registration that overrides your registration further down in the code
  • you are making use of one or more third party extension methods that do a ‘black box’ service registration to the IServiceCollection –  these may add one or more overriding registrations after your registration of a service type (note,  by default, the last registration of a service type will be the one that is returned if the consumer asks for a single instance … though if playing nicely, the third party will avoid this by using one of the variations of TryAdd extension methods)
  • you have registered the service type to use a lambda expression that returns the instance by resolving another service registration from the container within the expression
  • you have registered the service type by using a lambda expression that creates the instance for you either within the lambda or by using a factory class.

The other main consideration, (as Jamie said in the podcast),  is that if your class is publicly accessible and has a public constructor, there is nothing to stop someone (either a team member or your future self) from instantiating the instance (via the new keyword).

If this is the case, then it is advisable to put guard checks in place if receiving a constructor parameter set to null will cause your class to either behave differently or, more likely, throw an unexpected NullReferenceException somewhere within its code execution.

My Top 5 Null Injections

So with the above in mind, let’s look at some common scenarios I have picked out where the container could resolve a null either for the requested service type or one/some of the properties within a resolved instance.

1. Redirection to Another Service Via the Service Provider

You may have a type that has many interfaces through which you wish the container to resolve an instance (E.g. the Interface Segregation Principle has been applied).

In order to ensure each of the interface registrations return the same instance (and not a new instance for each of the interfaces), you need to register the class first and then subsequently register each interface with a lambda expression that uses the service provider to return the main class instance cast as the interface.

So how can this approach end up with the container returning  a null? In short, the lambda expressions are evaluated at runtime when the instance resolution is requested and do not explicitly check for nulls being returned.

However, more specifically with regards to getting another instance from the container, there is just one method, GetService on the IServiceProvider interface to retrieve an instance for you.

If GetService is used within the lambda expression, and the requested type has not been registered, it does not throw an exception. Instead, it will return a null which is then returned from the container to the consumer that requested the service type. Therefore, if the lambda just returns the result from GetService, the consumer will receive a null.

Whilst the IServiceProvider interface only has the one method, the extension methods provided by the ServiceProviderServiceExtensions class include GetRequiredService  and it’s generic counterpart GetRequiredService<T>.

These both perform a null check and throw an InvalidOperationException when a service redirection cannot be resolved.

Therefore, my advice is to always use the GetRequiredService<T> extension method if resolving an instance from the container within a lambda.


Another way to get or create instances from the container is the ActivatorUtilities class, but I won’t cover that here.


2. Other Lambda Expression Factory Resolutions

In addition to using a lambda expression to resolve another type from the service provider as shown above, we can use the same tactic to resolve an instance from all manner of instance creation methods or lookup systems.

You may be using a dedicated factory class to create an instance or taking some resolved instance and mapping or composing some other type instance from these constituent parts which may inadvertently result in a null.

Similarly, you may be using some form of cache or in memory lookup table that takes a key and returns the result. In some cases, if the value cannot be found, the lookup may return null instead of throwing an exception (E.g. a collection that inherits from NameObjectCollectionBase  or an implementation of IDistributedCache that in turn feeds some data into an object factory).

If this is the case, you may want to consider including a null check within the lambda that will either coalesce the result into a default NullObject instance or throw an exception at point of instance resolution.

If you don’t do this, the consumer will need to protect itself from a null parameter injection using guard clauses.

3. Black Box Extensions Methods

Many third party NuGet package libraries offer extension methods to perform service registration of services within the library. In many cases, these become ‘black box’ service resolutions.

As a consumer of these services, you have no guarantee that the service will not result in a null (due to one or more of the above scenarios).

Whilst you could look at the library source code if it is available on GitHub or though Source Link, this is going beyond what you should need to do as a consumer of a library.

Therefore, as a consumer, you need to protect yourself from null injection if this is going to cause your code to break.


The next two items are not direct null injections, but can cause member values on instances injected from the container to be set to null which may have a knock on consequence on your consuming class.


4. IEnumerable<T> Service Injection

If you (or a third party) have added multiple registrations for the same service type, your consumer will need to specify a constructor parameter of IEnumerable<T> where T is the service type in order to retrieve all the service resolutions.

At time of writing, this is documented on the Microsoft Docs page for DI fundamentals (though this may have moved if you are reading this in the future!)


You can also request multiple services directly from the container using the GetServices extension methods, but again handle with care!


Two things are worth checking when receiving an enumerable of service instances.

Firstly, if the required service type has not been registered, an exception will not be thrown when resolving the enumerable. You will get an IEnumerable<T>  instance (not a null)  but it will not yield any results when iterating over it as it will be empty.

If you cast the (empty) enumerable into an array or a collection based type and have logic that applies some form of matching (on the assumption that certain types have been registered), your code will probably blow up, so it is worth having some guards around this.

Secondly, for the reasons described above in the three direct null scenarios, one (or more) of the services in the returned enumeration may be null and therefore it is worth including a null check guard clause when iterating over the enumerable to confirm that each yielded instance in the enumerable is not null before using it.

5. Configuration Binding

This last source of null injection is an edge case that is not about a null being  injected, but properties on a resolved instance not being set due to a problem with the container.

If you use configuration binding to retrieve a configuration section and bind its children to properties of a class, you could end up with some of the properties (or child properties if hierarchical) being set to default values if the binding is unable to find a match.

This can come about in two ways:

  • The path to the configuration is not found
  • Properties do not have matching entries in the configuration

The first of these scenarios can happen due to errors or source code changes that mean that the whole object cannot mapped as it cannot find the root entry. (E.g. the IConfiguration.GetSection method relies on a string value to specify the section name and therefore any errors in the path will not be detected until runtime)

The second can also happen for the same reasons, but may also happen due to omission of a property within the configuration source (E.g. entry missing from appsettings.json).

Whichever way the mismatch happens, the consuming class need to protect itself from defaults due to the properties not being set (these defaults may be actual values on structs, null for reference types, or an unset nullable value types).

This protection may be done either by coalescing type default values into domain default values or making use of the various validation techniques you can apply to configuration objects that will throw an exception during or post binding.

Alternatively, you could pass the buck down to the end-consumer class to validate the configuration value(s) received.

Guarding Your Consumer Against Null Injection

I have put forward my personal Top 5 list of situations which justify writing guard clauses to check for constructor parameters.

How you perform these checks is up to you, but my approach is to use a mixture of

  • using null coalesce (?? or its C#8 sibling ??= ) into a fixed default value that your code can use
  • check for null injection (or null properties) and throw an ArgumentNullException or (if the context justifies) one of its sibling exceptions (ArgumentException or ArgumentOutOfRangeException). This checking can make use of either an explicit if statement (I like the new C# 9  ‘is not null’ pattern matching); or use the null coalescing operator that throws an exception on null (C# 7.0 onwards)

Whilst you still have to do some work, some of the boilerplate code can be reduced by using a library such as Steve (@ardalis) Smith’s GuardClauses NuGet package.

Conclusion

I hope the above list of null injection scenarios has been useful. If I think of any more, I will either post updates to this page or create another post in the future.

Using IConfigureOptions To Apply Changes to a Configuration

In one of my previous posts, Hiding Secrets in appsettings.json – Using a Bridge in your ASP.Net Core Configuration (Part 4) I had a comment from Anderson asking if I had considered IConfigureOptions<TOptions> as a way of injecting dependencies into the TOptions class that had been bound to a configuration section.

I had not come across the interface, so with an example provided by Anderson, I started to look further into it. Andrew Lock has post on his blog that describes how an implementation works which was a starting point for me.

IConfigureOptions<TOptions>

The IConfigureOptions<TOptions> interface has a single method (Configure) that takes an instance of TOptions where TOptions is a class that you will use for binding configuration values. If unfamiliar with this, please read my previous posts starting with Creating a Bridge to your ASP.Net Core Configuration from your Controller or Razor Page (Part 1).

In your implementation of IConfigureOptions<TOptions>, you are free to manipulate the options instance by changing property values. Your implementation can have a constructor that will take any dependencies you may need into order to set the properties. These constructor parameters will be resolved by the DI container. However, be aware of different scopes that may apply and how these are resolved. Read Andrew’s post for more info.

In the example below, I have:

  • a class MySettings which I will bind the configuration section to.
  • three implementations of IConfigureOptions<MySettings> which change the Setting1 string in different ways.
  • an interface IDateTimeResolver and class implementation to demonstrate the DI container injecting a resolved instance into the last of the IConfigureOptions<MySettings> implementations (and also serves as a  reminder that using DateTime.Now directly in your code is evil and should be wrapped in another class so it can be substituted when unit testing)

The interface/implementation mappings are registered with the DI container in the Startup class. Note the Confgure<MySettings>() and AddOptions() extension methods have been registered first.

What is interesting, is that multiple classes can implement the interface and, when registered with the DI container, be applied in the order they have been registered.

When I first saw this, I was confused as my understanding of DI registration was that the last registration is the one that is used to return the resolved instance by the DI container, so how could multiple registrations be applied? Of course, the penny dropped in that whatever is using these instances is not asking for the DI container to resolve a single instance, but is somehow iterating over the relevant registrations and resolving each registration in turn then calling the .Configure method.

A quick delve in Microsoft’s ASP.Net repository on GitHub, revealed the OptionsFactory class.

This pretty much confirmed the theory that the services collection is being iterated over, except you will notice that the constructor takes a parameter of IEnumerable<IConfigureOptions<TOptions>>. So what is resolving these multiple instances? 

Well it turns out to be the DI Container itself! I will go into more detail in a future post (as I don’t want to deviate from the main topic of this post)*, but in short, if your constructor takes an IEnumerable<T> as a parameter, then the DI container will use all registered mappings of T to resolve and inject multiple instances as an enumeration.

*Update – Since the post was written, Steve Gordon has written a great post that goes into more detail about the DI container and resolving multiple implementations  – see ASP.NET Core Dependency Injection – Registering Multiple Implementations of an Interface.

So in the OptionsFactory instance that will be created by ASP.Net using the DI container (where TOptions is MySettings, the class I have set up for configuration binding), the DI container will resolve the parameter of IEnumerable<IConfigureOptions<MySettings>> to our three registered implementations of IConfigureOptions<MySettings>. These will be injected and then the Create method in the factory will call the Configure method on each instance in order of registration in the Create method.

Therefore to demonstrate this, we will start with an appsettings.json that looks like this:

We can step through the three implementations as they are called by the factory Create method to see how the Settings1 value in MySettings is updated:

Example of how the Settings1 value changes as each configuration option is applied
Example of how the Settings1 value changes as each configuration option is applied

IConfigureNamedOptions<TOption> and IPostConfigureOptions<TOptions> 

The eagle eyed will have spotted that in the OptionsFactory, there is a check on each instance of IConfigureOption<TOption> to see if it is an implementation of IConfigureNamedOptions<TOption>.

This is very similar to IConfigureOption<TOption>, but the signature of the Configure method has an extra string parameter called name to point to a named configuration instance.

Once each of the registered implementations of IConfigureOption<TOption> has been processed, the factory then looks for implementations of the IPostConfigureOptions<TOptions> interface.

I plan on covering these two interfaces in a future post as I don’t want to make this post too long.

Summary

At first glance, this approach is similar to the bridge pattern approach I have described in my earlier posts in that the responsibility for changing the state values is delegated to an outside class.

However it is not a bridge as the properties of the underlying configuration instance are being changed. A bridge will intercept requests and then change the return value, leaving the underlying configuration instance unchanged.

When using the IConfigureOptions<TOptions> approach, the TOptions instance that is passed in through the parameter in the Configure method is being mutated. The changes will live on in the instance.

My personal opinion is that this not as clean as the bridge approach (but I guess I am biased).

I prefer to try to avoid potentially unexpected mutation of objects and instead return either a new instance of the object with property values copied from the existing into the new one or a substitute (e.g. the bridge). In other words, a functional approach where the input object is treated as immutable.

This leaves the original instance of the class in its initialised state which may be required by other functionality in the application that does not want the updated values or can be used by a different bridge class.

Hiding Secrets in appsettings.json – Using a Bridge in your ASP.Net Core Configuration (Part 4)

This is part 4 of a series where I have been looking at moving to a SOLID approach of implementing configuration binding in ASP.Net Core using a bridging class to remove the need for consumers of the configuration object to use IOptions<T>  or IOptionsSnapshot<T>. If you have arrived at this page from a search engine, I recommend looking at the previous posts Part 1Part 2 and Part 3 before moving onto this one.

In this post I move onto looking at injecting some functionality into the bridge class to decrypt settings and validate the settings read. Lastly I show registering the bridge class via multiple fine grained interfaces.

To follow along with this post, I suggest you download the full solution source code from the Github repo at https://github.com/configureappio/ConfiguarationBridgeCrypto as there is far too much code to display in this post.

Adding More DI Services

I will start with the changes to the Startup.cs ConfigureServices method which gives a structure to the changes we will be making.

The main highlights are:

  • A factory class is registered via its interface to decrypt values read from the settings
  • A class via its interface is registered to validate the settings
  • The bridge class is registered via an aggregate interface
  • A resolution lambda is registered for each of the component interfaces that make up the aggregate interface.

The classes and interfaces are now looked at in more detail below.

Encrypted Settings

For the purpose of this demonstration, I am assuming that for one reason or another,  the standard secure configuration providers such as Azure Key Vault cannot be used for one reason or another, so we are having to deal with encrypting the settings ourselves.

Heath Warning !!! 

In this demo, the encrypted settings are in the main appsettings.json. DO NOT DO THIS IN THE REAL WORLD!  Stick to the mantra that you should not put any secrets in your code source control. Always think, “Would I be OK with the source code repo going open source?”

In the source code, I have included code to read from an external file outside of the web code location so that the secrets are maintained outside of source code, but the settings could come from environmental variables or the command line. If copying the source code that accompanies this post, I suggest copying the appsettings.json to the location shown and removing outside of source code. It is up to you where to store it.

To keep things clean, all encrypted values are held in a dictionary within the AppSettings class in a property called Secrets. It will be the responsibility of the bridge class to decrypt the secrets and inject them into the properties exposed via the interfaces (more on this later).

The appsettings.json and matching MyAppSettings class will therefore look like this:

At this point, the DI container has been configured to return IOptionsSnapshot<MyAppSettings> by the services.Configure<MyAppSettings> line in the startup code.

The secrets dictionary key/value pairs have been encrypted using a hash for the key and AESManaged for the value. Both have then been Base64 encoded so that they can be cleanly represented in the JSON.

Decrypting the Settings

In order for the bridge class to decrypt the dictionary, we will need a class that will get injected into the bridge class via an ICryptoAlgorithm interface. To keep things flexible, we will use a factory pattern to create the decryptor instance.

In the example code, I register the results of calling the factory as a singleton that. However, you may want to register the factory and inject that into classes if you want to use multiple cryptographic algorithms or salt/password combinations.

Once we have the decryptor registered, we need to apply it to the settings. For this, we will have a SettingsDecryptor class that implements an ISettingsDecrypt interface.

This is registered with the DI container services ready to be used by the bridge class. The Decrypt method in the example takes a plain text version of the dictionary key then

  • hashes it with the method exposed by the injected decryptor,
  • looks up the hashed key (that is in the appsettings.json)
  • then decrypts the value for that key.

So with these pieces in place, we have the components for decrypting the settings in the bridge class.

Before looking at the bridge, we will look at injecting functionality to validate the settings.

Validating Settings

By injecting a settings validator into the bridge class, we have the ability to catch any problems before the rest of our code tries to use the values (encrypted or not).

The class implements an interface that validates the settings and returns a boolean to indicate success or failure. If validation has failed, an AggregateException instance is available that holds one or more validation exceptions.

There are more elegant ways in which this can be approached, but I used this approach for simplicity to illustrate the principle of injecting a validator into the bridge.

Once the validator is registered as a DI service, we are now ready to register the bridge class that takes both the decryptor and validator.

Take It To The Bridge

The example above is fairly self-explanatory at a high level. The constructor takes the IOptionsShapshot<MyAppSettings> to get the settings class that has been constructed by the DI service using the Options pattern. This gives us access to the bound object.

We then have a decryptor which is stored as an instance field for use by the property getters to decrypt values read from the settings object.

We then have a validator instance which we call immediately to validate the settings and throw an exception if there is a problem.

The three properties exposed are proxies to the underlying settings class, with decryption taking place where necessary.

Interface Segregation

The bridge class implements the IAppSettingsResolved interface which is an aggregate of three other interfaces.

This has been done to illustrate that the settings can be registered as multiple interfaces to allow for interface segregation as part of the SOLID approach. E.g. if a controller is only interested in the SQL Server connection string, it can just ask for that rather than the full  IAppSettingsResolved. This makes it easier to implement just the required functionality in any mocks when unit testing or any other implementation you may want to register.

The registration above uses the service for IAppSettingsResolved to resolve the three other interfaces.

Conclusion

Having described the working parts above, we can come back to the ConfigureSevices method to tie it together.

Here we have done the following

  • Registered an IOptionsSnapshot<MyAppSettings> using the Configure method to bind the “MyAppSettings” configuration section to an object instance
  • Registered a decryption algorithm
  • Registered a class instance to decrypt the key/value pairs in the Secrets dictionary using the algorithm
  • Registered a class instance to validate the settingsop
  • Registered a class instance to act as the bridge/proxy to the IOptionsSnapshot<MyAppSettings> and decrypt the values
  • Registered the resolved bridge class using its multiple exposed interfaces for finer grained use

With the last of these in place, our controllers and any other dependant classes can choose whether to get the settings as a whole via IAppSettingsResolved or one of the finer grained interfaces

  • IAppSettings – the non-encrypted values
  • ISqlConnectionString – the decrypted SQL connection string
  • IOracleConnectionString – the decrypted Oracle connection string

Taking Things Further

That wraps up this series of posts for now, but you may want to take things further now that the settings class can have functionality injected into it.  Possibilities include

  • Using connection string builders to create connection strings using multiple properties (some encrypted, some not) from the bound object
  • Using the ICryptoFactory instead of ICryptoAlgorithm to use multiple algorithms for different properties

Don’t forget, the full source code including a WPF app to encrypt the settings dictionary can the downloaded from the Github repo at https://github.com/configureappio/ConfiguarationBridgeCrypto

Thanks for reading.