Let's talk about the differences between Umbraco 8 & 9! Today we will be looking into Dependency Injection / Inversion of Control in Umbraco 8 and 9. How does it work, and what has changed between the two versions? Let's talk about that!
Umbraco 8 - LightInject
Compared to previous versions of Umbraco, Umbraco 8 supports dependency injection out of the box using LightInject. LightInject is a lightweight Inversion of Control (IoC) container, which is represented in Umbraco using a Composition.
Most of (if not all) the Umbraco services, helpers, managers and other goodies Umbraco provides can be injected out of the box, including but not limited to the UmbracoHelper, ExamineManager and ILogger implementation used in your application. Be sure to check out the Umbraco Docs to see some examples of these implementations.
To register our own dependency to be available for injection, we will be using a composer (implementing the IUserComposer interface) to register our service. Within our composer we specify the Implementation of the class that we want to have available for injection. We can specify an Interface if we want to have the ability to change the implementation of our class in the future (which is the way I would recommend setting it up, in most cases!).
Let's take a look at the code snippet from the official Umbraco documentation for an example on how to register a service.
We also have the ability to specify a lifetime for whatever we wish to register. Umbraco 8 has the following 4 options:
- Transient, which always creates a new instance (and is the default if left empty)
- Request, which creates a unique instance per request (or injection)
- Scope, which creates a unique instance per scope (or web request)
- Singleton, which creates a unique instance per container (or per application)
Once we have registered whatever we wish we register, we can now inject them in a class constructor:
As easy as that! We can now register our own dependencies into our Umbraco 8 application!
Umbraco 9 - ASP.NET Core
Umbraco 9 supports dependency injection out of the box, but instead of requiring a third party library for this, Umbraco uses the built-in dependency injection from ASP.NET Core. This means we can use all the shiny features ASP.NET Core offers us out of the box without requiring any external packages!
Umbraco has its own specific abstraction on-top of ASP.NET Core's IServiceCollection used for dependency injection called IUmbracoBuilder, whose purpose is to aid in adding and replacing Umbraco-specific services, including notification handlers which we discussed in our previous Blog about Events & Notifications.
There are two main options for registering our new notification handler:
- Directly (or indirectly) in the ConfigureServices method of our Startup class.
- Using a Composer implementing the IComposer interface, giving us access to the IUmbracoBuilder
If we wish to add something Umbraco specific, it is important that we do this in the "AddUmbraco" builder chain in our Startup.cs class, using the IUmbracoBuilder extension methods. If we do not require anything Umbraco related for our services, we can inject them outside of our "AddUmbraco" chain.
The above mentioned method is great when it comes to registering dependencies in our own site, but if we want to create a package/class library to be used with other projects, we do not have access to the Startup class. To resolve this we can use a composer (implementing IComposer), giving us access to the IUmbracoBuilder implementation just like in our Startup class. Within our composer we can use the appropriate extension methods of IUmbracoBuilder to register the handlers/services/managers of our choosing!
(Tip! If your Startup.cs or composer is starting to get cluttered and hard to manage, you can write your own extension methods for IUmbracoBuilder to keep them all in as little as a single call!)
Within Umbraco 9 we have the option to choose between 3 different lifetimes for our services:
- Transient, which always creates a new instance on each injection
- Scope, which creates a unique instance per scope (or web request)
- Singleton, which creates a single instance per container (or per application)
Just like in our Umbraco 8 snippet, we can inject our service into either a controller or another service using the class constructor!
We also have the option to manually access our services from our IoC container using the RequestServices property of HttpContext as shown below. While constructor injection is the recommended way of getting our injected services, it is good to known we have other options!
Summary
In essence the way Dependency Injection works between Umbraco 8 and 9 has not changed all that much. While the way we access our dependencies stayed the same, the way we register our services, handlers and managers has changed from being registered in a Composer using an instance of Composition, to being registered to the IUmbracoBuilder within our Startup class (or using a Composer class). We no longer depend on a third party library for Inversion of Control, and can fully rely on the familiar experience of the built-in ASP.NET Core injection! 🔥
If you have any further questions, feel free to contact me over at my socials available at the Contact page, and I'd love to hear about the amazing creations you're able to come up with! 😄
Sources used in this article:
Umbraco Documentation, https://our.umbraco.com/Documentation/Reference/Using-Ioc/ & https://our.umbraco.com/Documentation/Reference/Using-Ioc/index-v8
Microsoft Documentation, https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-5.0#service-lifetimes
Dependency Injection ASP.NET Core, https://www.tutorialsteacher.com/core/dependency-injection-in-aspnet-core