Dependency Injection (DI) is a simple concept that can lead to confusion as the use-cases become more convoluted so here is an example from real-life where I have not used DI and have immediately hit a wall that is easiest fixed by injecting a service.

What is Dependency Injection

In the simplest terms, any "services" that a class needs to use are injected, ideally in the constructor but also potentially into an individual method that consumes the service or even via a factory for more complex scenarios (note that some people suggest that DI factories are a code smell!).

DI is an inversion of control (IoC) since rather than the class deciding that it needs to use, e.g. a SQL server to provide data, instead the data source is abstracted (usually via an interface) and something at a higher level decides whether to inject a SQL server, MySql server or something else including mock objects for unit testing.

We usually use a IoC container, such as Unity (not the games engine) or Autofac to help us with the inevitable spider's web of service dependencies where A depends on B which depends on C and D etc. but it is not mandatory for the simplest cases. In Dotnet Core, there is a built-in IoC container which doesn't have every functionality that is available in other containers, although it does force rethinking of abstractions and we have managed so far to live with it and avoid having another container included.

My Scenario

I have a web service that uses X509 certificates to sign Json Web Tokens. Originally, I used files for the certificates for historical reasons. They are read in from file, converted to X509Certificate2 etc. What I realised early on was that doing this in unit tests was tricky because the unit test runner is not running in the web service context and will not be able to read the files from App_Data in the way that the web service can.

I abstracted the location of these to an IKeyFileSource, which was simply a way to obtain the path to the certificates. In production, this uses an AppDataSource to read from App_Data and in the unit tests it uses a TestDataSource, which loads the files from the unit test project instead.

All great so far, except...

I now have a ticket to get the X509 certs from the certificate store instead of files. Obviously this makes more sense and it simply replaces:

var cert = new X509Certificate2(
                string.Format(options.Value.Cert1FileName,
                    keyFileSource.CertPath),
                    options.Value.Cert1Password
                )

with

var cert = store.Certificates.Find(X509FindType.FindByThumbprint, options.Value.Cert1Thumbnail, true)[0];

But..

Unit Test Problem

The unit test problem often occurs after feeling smug that you have made the application work using a minimum amount of code and then realised it doesn't work again!

Why not? Again, the Current User of the unit tests is not the same as the Current User of the web service or the Current User of me adding the certs into my Current User store on the local machine so it alls fails again.

What I noticed this time, however, was that my abstraction of IKeyFileSource was not high enough and I was still assuming that I was getting certs from file. In other words, I was not injecting the "file" service that I was using, even though DI says that all services should be injected.

My new abstraction needs to be ICertificateSource which will probably contain a single property called Certificate, which in the case of a FileCertificateSource will read it from file and if a StoreCertificateSource will read it from the store instead. The best thing about this is that I can do whatever I need to do to make the unit tests work, either injecting the file or if I can use the store, I can use the store. It also means that both the unit test part and the web service code it is testing can do the same thing.