Skip to content
167 changes: 126 additions & 41 deletions 14/umbraco-cms/reference/using-ioc.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,37 +4,66 @@ description: Inversion of Control/Dependency Injection in Umbraco

# Inversion of Control / Dependency injection

Umbraco v9+ supports dependency injection out of the box. Umbraco uses the [ASP.NET Core built-in dependency injection](https://docs.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-5.0#service-lifetimes). This means that you don't have to install external packages to register and use your dependencies. If you're familiar with ASP.NET Core, the experience will be similar.
Umbraco supports dependency injection out of the box using the [ASP.NET Core built-in dependency injection](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection). This means that working with dependencies in Umbraco is similar to working with them in ASP.NET Core.

`IUmbracoBuilder` is a Umbraco-specific abstraction on top of the `IServiceCollection`, its purpose is to aid in adding and replacing Umbraco-specific services, such as notification handlers, filesystems, server role accessor, and so on. You can access the `IServiceCollection` directly to add your custom services through the `Services` property, see below for a concrete example:
`IUmbracoBuilder` is a Umbraco-specific abstraction on top of the `IServiceCollection`. Its purpose is to aid in adding and replacing Umbraco-specific services, such as notification handlers, filesystems, server role accessors, and so on. You can access the `IServiceCollection` directly to add custom services through the `Services` property. See below for a concrete example:

```csharp
IUmbracoBuilder.Services
```

## Registering dependencies

There are two strategies for registering your own dependencies to the container, which one you should use depends on whether you're making a package, or making custom services for your own site.
There are different strategies for registering your dependencies and not one strategy is better than the other.

### Registering dependencies for your site
In this article, we will cover the following three strategies:

When working with your site, and not a package, the recommended way to register dependencies is with the `Program.cs` file:
* [Registering dependencies in the `Program.cs` file](#registering-dependencies-in-the-programcs-file)
* [Registering dependencies in a composer](#registering-dependencies-in-a-composer)
* [Registering dependencies in bundles](#registering-dependencies-in-bundles)

Which strategy to choose depends on the scenario requiring dependency registration.

### Choosing a strategy for registering dependencies

Are you **[working directly on your site](#registering-dependencies-in-the-programcs-file)**? You can choose whichever strategy you prefer working with.

Are you **[building a package](#registering-dependencies-in-a-composer)** and do not have access to the `Program.cs` file? In this case, you have the option to register the dependencies in a composer.

Are you in a situation where you need to **[register more than a few dependencies](#registering-dependencies-in-bundles)**? You can bundle your dependencies in custom extension methods and register them in a single call.

### Registering dependencies in the `Program.cs` file

When working with your Umbraco site, dependencies can be registered within the `Program.cs` file.

In the example below, a custom notification handler is added to the `CreateUmbracoBuilder()` builder chain:

{% code title="Program.cs" %}

```csharp
builder.CreateUmbracoBuilder()
.AddBackOffice()
.AddWebsite()
.AddDeliveryApi()
.AddComposers()
// If you need to add something Umbraco specific, do it in the "AddUmbraco" builder chain, using the IUmbracoBuilder extension methods.
// When you need to add something Umbraco-specific, do it in the "AddUmbraco" builder chain, using the IUmbracoBuilder extension methods.
.AddNotificationHandler<ContentTypeSavedNotification, ContentTypeSavedHandler>()
.Build();
```

### Registering dependencies in packages
{% endcode %}

When working with packages, you won't have access to the `Program.cs` file. Instead you must use a [composer](../implementation/composing.md) to register your own dependencies in the container.
Using the `Services` property, or appropriate extension method, of the `IUmbracoBuilder`:
{% hint style="info" %}
Learn more about the uses of the `Program.cs` file in [the official ASP.NET Core Fundamentals documentation](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/?view=aspnetcore-8.0&tabs=windows).
{% endhint %}

### Registering dependencies in a composer

When working with packages, you do not have access to the `Program.cs` file. Instead, you can use a [composer](../implementation/composing.md) to register your dependencies.

Below is an example of a composer using the `Services` property of the `IUmbracoBuilder`:

{% code title="MyComposer.cs" %}

```csharp
using IOCDocs.NotificationHandlers;
Expand All @@ -56,13 +85,21 @@ public class MyComposer : IComposer
}
```

{% endcode %}

{% hint style="info" %}
Remember to add `Umbraco.Cms.Core.DependencyInjection` and `Microsoft.Extensions.DependencyInjection` as 'using' statements where you register your services, to gain access to the `IUmbracoBuilder`, its extension methods, and the Microsoft \`IServiceProvider.
To access the `IUmbracoBuilder`, you need to add `Umbraco.Cms.Core.DependencyInjection` and `Microsoft.Extensions.DependencyInjection` as using statements when registering your services. This, in turn, will also give you access to the `IUmbracoBuilder` extension methods as well as the Microsoft `IServiceProvider`.
{% endhint %}

### Builder extension methods
### Registering dependencies in bundles

Depending on your scenario, you may have a lot of dependencies you need to register. In this case, your `Program.cs` or Composer might become cluttered and hard to manage. A great way to manage multiple services is by creating your own custom extension methods for the `IUmbracoBuilder`. This way you can group similar dependencies in extension methods and register them all in as little as a single call:
Depending on your scenario, you may have a lot of dependencies you need to register. In this case, your `Program.cs` or Composer can become cluttered and hard to manage.

You can manage multiple services in one place by creating your custom extension methods for the `IUmbracoBuilder`. This way you can bundle similar dependencies in extension methods and register them all in a single call.

In the following code sample two dependencies, `RegisterCustomNotificationHandlers` and `RegisterCustomServices` are bundled together in a custom `AddCustomServices` extension method.

{% code title="MyCustomBuilderExtensions.cs" %}

```csharp
using IOCDocs.NotificationHandlers;
Expand All @@ -75,20 +112,23 @@ namespace IOCDocs;

public static class MyCustomBuilderExtensions
{
// The first dependency is registered
public static IUmbracoBuilder RegisterCustomNotificationHandlers(this IUmbracoBuilder builder)
{
builder.AddNotificationHandler<ContentTypeSavedNotification, ContentTypeSavedHandler>();
{...}
return builder;
}

// The second dependency is registered
public static IUmbracoBuilder RegisterCustomServices(this IUmbracoBuilder builder)
{
builder.Services.AddSingleton<IFooBar, Foobar>();
{...}
return builder;
}

// The two dependencies are bundled together
public static IUmbracoBuilder AddCustomServices(this IUmbracoBuilder builder)
{
builder.RegisterCustomNotificationHandlers();
Expand All @@ -98,28 +138,42 @@ public static class MyCustomBuilderExtensions
}
```

{% endcode %}

{% hint style="info" %}
It is not required to have an interface for your dependency:
It is not required to have an interface registering your dependencies:

```csharp
services.AddSingleton<Foobar>();
```

{% endhint %}

Now you can call your `AddCustomServices` in either the `Program.cs` file, or your composer like so:
With the dependencies bundled together, you can call the `AddCustomServices` method in either the `Program.cs` file or your composer:

{% tabs %}
{% tab title="Program.cs" %}

{% code title="Program.cs" %}

```csharp
builder.CreateUmbracoBuilder()
.AddBackOffice()
.AddWebsite()
.AddDeliveryApi()
.AddComposers()
// Register all our custom services in one go.
// Register all custom dependencies in one go using the custom extension method
.AddCustomServices()
.Build();
```

{% endcode %}

{% endtab %}
{% tab title="Composer" %}

{% code title="MyComposer.cs" %}

```csharp
using Umbraco.Cms.Core.Composing;
using Umbraco.Cms.Core.DependencyInjection;
Expand All @@ -130,46 +184,51 @@ public class MyComposer : IComposer
{
public void Compose(IUmbracoBuilder builder)
{
// Register all our custom services in one go.
// Register all custom dependencies in one go using the custom extension method
builder.AddCustomServices();
}
}
```

### Service lifetime
{% endcode %}

{% endtab %}
{% endtabs %}

## Service lifetime

During registration you have to define the lifetime of your service:
During registration of your dependencies, you have to define the lifetime of your service:

```csharp
IServiceCollection.AddTransient<TService, TImplementing>();
IServiceCollection.AddScoped<TService, TImplementing>();
IServiceCollection.AddSingleton<TService, TImplementing>();
```

There is three possible lifetimes:
There are three possible lifetimes:

* Transient - always creates a new instance
* A new instance will be created each time it's injected.
* Scoped - one unique instance per web request (connection)
* Scoped services are disposed at the end of the request
* Be very careful not to resolve a scoped service from a singleton, since it may cause it to have an incorrect state in subsequent requests.
* Singleton - one unique instance for the whole web application
* The single instance will be shared across all web requests.
| Name | Lifetime | Description |
|---|---|---|
| **Transient** | Creates a new instance | A new instance will be created each time it's injected. |
| **Scoped** | One unique instance per web request (connection) | Scoped services are disposed of at the end of the request. Be careful not to resolve a scoped service from a singleton, as it may lead to an incorrect state in subsequent requests. |
| **Singleton** | One unique instance for the whole web application | The single instance will be shared across all web requests. |

For more information, have a look at the official [Microsoft documentation](https://docs.microsoft.com/en-us/dotnet/core/extensions/dependency-injection#service-lifetimes).
For more information, read the official [Microsoft documentation on dependency injections](https://docs.microsoft.com/en-us/dotnet/core/extensions/dependency-injection#service-lifetimes).

## Injecting dependencies

Once you have registered your services, factories, helpers or whatever you need for you application, you can go ahead and inject them where needed.
Once you have registered the dependencies inject them into your project where needed.

### Injecting dependencies into a class

If you need to inject your service into a controller, or another service, you'll do so through the class
If you need to inject your service into a controller or another service, you will do so through the class.

{% hint style="warning" %}
The example below uses UmbracoApiController which is obsolete in Umbraco 14 and will be removed in Umbraco 15.
{% endhint %}

{% code title="FooController.cs" %}

```csharp
using IOCDocs.Services;
using Umbraco.Cms.Web.Common.Controllers;
Expand All @@ -193,7 +252,11 @@ public class FooController : UmbracoApiController
}
```

If you place a breakpoint on `var bar = _foobar.Foo()`, open `/Umbraco/Api/foo/foo` in your browser and inspect the variable, you'll see that the value is `bar`, which is what you'd expect since all the `Foobar.Foo()` method does it to return `Bar` as a string:
{% endcode %}

If you place a breakpoint on `var bar = _foobar.Foo()`, open `/Umbraco/Api/foo/foo` in your browser and inspect the variable, you'll see that the value is `bar`. This is what you would expect as all the `Foobar.Foo()` method does is to return `Bar` as a string:

{% code title="Foobar.cs" %}

```csharp
namespace IOCDocs.Services;
Expand All @@ -204,18 +267,22 @@ public class Foobar : IFooBar
}
```

{% endcode %}

### Injecting dependencies into a View or Template

You might need to use services within your templates or views, fortunately, you can inject services directly into your views using the `@inject` keyword. You can for example inject the `Foobar` from above into a view like so:
In some cases you might need to use services within your templates or view files. Services can be injected directly into your views using the `@inject` keyword. This means that you can inject the `Foobar` from above into a view like shown below:

{% code title="Home.cshtml" %}

```html
@using Umbraco.Cms.Web.Common.PublishedModels;
@inherits Umbraco.Cms.Web.Common.Views.UmbracoViewPage<ContentModels.Home>
@using ContentModels = Umbraco.Cms.Web.Common.PublishedModels;

@* Add a using for the namespace of the service *@
@* Add a using statement for the namespace of the service *@
@using IOCDocs.Services
@* Now you can inject it *@
@* Inject the service *@
@inject IFooBar _fooBar

@{
Expand All @@ -225,19 +292,25 @@ You might need to use services within your templates or views, fortunately, you
<h1>@_fooBar.Foo()</h1>
```

If you then load the page which uses this template you'll see a heading with "Bar", which we got from our service.
{% endcode %}

Note that in order to use our service we also have to add a using statement for the namespace of the service.
When loading a page using the template above, you will see the "Bar" heading which is retrieved from the service.

{% hint style="info" %}
To use the service a using statement for the namespace of the service needs to be added.
{% endhint %}

## Other things you can inject

Most of (if not all) the Umbraco goodies you work with every day can be injected. Here are some examples.
In this section, you can find examples of what you can inject when working with Umbraco.

### UmbracoHelper

[Read more about the UmbracoHelper](querying/umbracohelper.md)

`UmbracoHelper` is a scoped service, therefore you can only use it in services that are also scoped, or transient. To get UmbracoHelper you must inject `IUmbracoHelperAccessor` and use that to resolve it:
The `UmbracoHelper` is a scoped service, which means you can only use it in services that are also scoped or transient. To get the UmbracoHelper you must inject `IUmbracoHelperAccessor` and use that to resolve it:

{% code title="MyCustomScopedService.cs" %}

```csharp
using System.Collections.Generic;
Expand Down Expand Up @@ -283,13 +356,17 @@ public class MyCustomScopedService
}
```

{% endcode %}

{% hint style="info" %}
The use of the UmbracoHelper is only possible when there's an instance of the UmbracoContext. [You can read more here](../implementation/services/).
Using the UmbracoHelper is only possible when there is an instance of the UmbracoContext. [You can read more in the implementation article about services](../implementation/services/).
{% endhint %}

### ExamineManager

[Read more about examine](searching/examine/).
[Read more about the ExamineManager in the Searching articles](searching/examine/).

{% code title="SearchService.cs" %}

```csharp
using System;
Expand Down Expand Up @@ -354,9 +431,13 @@ public class SearchService : ISearchService
}
```

{% endcode %}

### ILogger

[Read more about logging](../fundamentals/code/debugging/logging.md)
[Read more about logging in the debugging section](../fundamentals/code/debugging/logging.md)

{% code title="Foobar.cs" %}

```csharp
using System;
Expand All @@ -380,6 +461,10 @@ public class Foobar : IFooBar
}
```

{% endcode %}

## Using DI in Services and Helpers

[Services and Helpers](../implementation/services/) - For more examples of using DI and gaining access to Services and Helpers, and creating your own custom Services and Helpers to inject.
In the [Services and Helpers documentation](../implementation/services/), you can find more examples of using dependency injection and gaining access to the different services and helpers.

You will also find information about creating custom services and helpers to inject and use in your Umbraco project.