Phone Handle Icon
Call Us
Back to all

Coding Abstractions Part 3: Dependency Injection

June 13, 2019
By
Steve Danner
Back to all
Share this article

This is the third and final post in a series of posts about coding abstractions into your applications. Here are the links to the first 2 posts in the series:

This final post is going to get into the technical concept of Dependency Injection. If you are familiar with Microsoft's Managed Extensibility Framework, or have used tools like AutoFac or Ninject before, these concepts will seem familiar. Unlike our previous posts, this one will focus much more on the coding aspect rather than the business perspective as we've already covered the business case in previous posts.

What we're really talking about with this set of posts is "extensibility". The abstraction points within an application are going to allow other developers to come in after your application has been in Production, and create add-ons or alter existing functionality without rebuilding the entire application. Going back to our sample Cloud file sync application, in part 2, we built out an extensibility point, or "contract" as well as the first "provider". Let's say a few years have passed, our company has moved away from OneDrive, and now need to build out another provider for Google Drive. If we did a few things properly early on when we were crafting the original application, one should not have to rebuild the entire application at all to switch to our new provider! There are 2 pieces to the puzzle to make this work.  The first is to ensure that we properly architected our original provider model to have ALL of the necessary properties and methods that would be needed for ANY provider. That provider model can also NOT be tightly bound to our original provider, or we could run into unforeseen issues when we change providers, as the consuming application would be expecting code or functionality that our new providers just won't have!

The second piece of the puzzle is that our original application must adhere to, is that it needs to use some kind of dependency injection to "inject" our application with the desired provider when our application runs. While Microsoft's Managed Extensibility Framework is a great library to do this exact type of plumbing for you, you have to learn a lot of the terminology and lingo that Microsoft has wrapped around it. Rather than take the time to understand what "Imports", "Exports" and "Parts" are, let's just use a super-simple method to use AutoFac, one of the most common dependency injection frameworks out there. Generally speaking, many applications are likely to already be using a dependency injection container anyway. However, the most common pattern you will see developers use is to use a class to hard-code all of an application's runtime dependencies.  For a normal, single use application that you don't plan on extending, this is fine. You can inject different dependencies in your unit tests, you get intellisense when you're coding those dependencies, and it's keeping all your dependency injection contained to 1 file, which is good practice. Here is a quick sampling of AutoFac being used in a traditional method in an application's startup routine.

var builder = new Autofac.ContainerBuilder();
builder.RegisterType<OneDriveProvider>().As<ICloudProvider>().InstancePerLifetimeScope();
var container = builder.Build();
System.Web.Mvc.DependencyResolver.SetResolver(new AutofacDependencyResolver(container));

However, when we're working with an extensibility model, and we don't want to recompile our entire application just to change out our provider, so we're effectively going to change just the second line of code above to accomplish this. AutoFac provides a method of dependency injection that allows us to instantiate a new provider with a Type object, that is created with a string that represents a fully-qualified class name of a class. Using this method, we can configure the provider class with our configuration file, and the application can initialize it without even having a direct reference to our new provider's assembly! In this example, here are the steps we will take to swap out our old OneDriveProvider to our new GoogleProvider class:

  1. Create a brand new .NET assembly. Reference the assembly that contains the ICloudProvider interface so that we can implement it in our new provider class.
  2. Copy the new assembly to a folder where the application will find our assembly. This link provides more information about how the .NET Framework is going to find or "probe for" assemblies. For a web application, the best bet is to just put it in the BIN directory, for a desktop application, your best bet is to just put it alongside the executable.
  3. Add a configuration key for your provider class.
  4. Use the AutoFac method of late-binding your class (ie - instantiating a class without a direct reference) with a fully qualified class string.

So to get our example above changed for using late-binding. We will just add a configuration key to designate our new provider class, and use the other AutoFac overload for late binding the new provider.

Configuration file:

<add key="ICloudProviderClass" value="MyNewAssemblyName.GoogleCloudProvider" />

Updated AutoFac code:

var builder = new Autofac.ContainerBuilder();
var className = ConfigurationManager.AppSettings["ICloudProviderClass"];
builder.RegisterType(Type.GetType(className)).As<ICloudProvider>().InstancePerLifetimeScope();
var container = builder.Build();
System.Web.Mvc.DependencyResolver.SetResolver(new AutofacDependencyResolver(container));

This method of late binding is extremely powerful. We commonly develop code using direct references to our dependencies, so this can be a different kind of methodology to grasp. Once you do, however, you will see just how much more flexibility it can give you, removing the need for massive, 10+ project solutions, and splitting up workloads like never before!  If you can grasp these concepts, you'll be on your way to developing highly extensible applications that allow you can create new functionality with minimal effort!