SOLID In C# – Dependency Inversion

This article is part of a series on the SOLID design principles. You can start here or jump around using the links below!

S – Single Responsibility
O – Open/Closed Principle
L – Liskov Substitution Principle
I – Interface Segregation Principle
D – Dependency Inversion

OK, let’s just get the Wikipedia definitions out of the way to begin with :

  1. High-level modules should not depend on low-level modules. Both should depend on abstractions (e.g. interfaces).
  2. Abstractions should not depend on details. Details (concrete implementations) should depend on abstractions.

While this may sound complicated, it’s actually much easier when you are working in the code. Infact it’s probably something you already do in (Especially in .NET Core) but you don’t even think about.

In simple terms, imagine we have a service that calls into a “repository”. Generally speaking, that repository would have an interface and (especially in .NET Core) we would use dependency injection to inject that repository into the service, however the service is still working off the interface (an abstraction), instead of the concrete implementation. On top of this, the service itself doesn’t know anything about how the repository actually gets the data. The repository could be calling a SQL Server, Azure Table Storage, a file on disk etc – but to the service, it really doesn’t matter. It just knows that it can depend on the abstraction to get the data it needs and now worry about any dependencies the repository may have.

Dependency Inversion vs Dependency Injection

Very commonly, people mix up dependency injection and dependency inversion. I know many years ago in an interview I was asked in an interview to list out SOLID and I said Dependency Injection as the “D”.

Broadly speaking, Dependency Injection is a way to achieve Dependency Inversion. Like a tool to achieve the principle. While Dependency Inversion is simply stating that you should depend on abstractions, and that higher level modules should not worry about dependencies of the lower level modules, Dependency Injection is a way to achieve that by being able to inject dependencies.

Another way to think about it would be that you could achieve Dependency Inversion by simply making liberal use of the Factory Pattern to create lower level modules (Thus abstracting away any dependencies they have), and have them return interfaces (Thus the higher level module depends on abstractions and doesn’t even know what the concrete class behind the interface is).

Even using a Service Locator pattern – which arguably some might say is dependency injection, could be classed as dependency inversion because you aren’t worrying about how the lower level modules are created, you just call this service locator thingie and magically you get a nice abstraction.

Dependency Inversion In Practice

Let’s take a look at an example that doesn’t really exhibit good Dependency Inversion behaviour.

The problem with this code is that MyService very heavily relies on concrete implementation details of the PersonRepository. For example it’s required to pass it a connection string (So we are leaking out that it’s a SQL Server), and a connection timeout. The PersonRepository itself allows a “ConnectToDatabase” method which by itself is not terribly bad, but if it’s required to call “ConnectToDatabase” before we can actually call the “GetAllPeople” method, then again we haven’t managed to abstract away the implementation detail of this specifically being a SQL Server repository.

So let’s do some simple things to clean it up :

Very simple. I’ve created an interface called IPersonRepository that shields away implementation details. I’ve taken that repository and (not shown) used dependency injection to inject it into my service. This way my service doesn’t need to worry about connection strings or other constructor requirements. I also removed the “ConnectToDatabase” method from being public, the reason being my service shouldn’t worry about pre-requisites to get data. All it needs to know is that it calls “GetAllPeople” and it gets people.

Switching To A Factory Pattern

While writing this post, I realized that saying “Yeah so I use this magic thing called Dependency Injection” and it works isn’t that helpful. So let’s quickly write up a factory instead.

Obviously not as nice as using Dependency Injection, and there are a few different ways to cut this, but the main points of dependency inversion are still there. Notice that still, our service does not have to worry about implementation details like connection strings and it still depends on the interface abstraction.

What’s Next

That’s it! You’ve made it to the end of the series on SOLID principles in C#/.NET. Now go out and nail that job interview!

Join over 3.000 subscribers who are receiving our weekly post digest, a roundup of this weeks blog posts.
We hate spam. Your email address will not be sold or shared with anyone else.

Leave a Reply

Your email address will not be published. Required fields are marked *