Open sandboxFocusImprove this doc

Injecting dependencies

Dependency injection is one of the most prevalent patterns in .NET. Prior to .NET Core, the community developed several frameworks with varying features and coding patterns. Since then, Microsoft.Extensions.DependencyInjection has emerged as the default framework.

Unlike its predecessors, Microsoft.Extensions.DependencyInjection doesn't rely on custom attributes on fields and properties. Instead, it requires you to add dependencies to the class constructor and store them as fields. This requirement can lead to boilerplate code, especially if there are numerous dependencies in a complex class hierarchy. Migrating your code from an attribute-based framework to a constructor-based one can also be tedious.

To reduce this boilerplate, use the [Dependency] aspect from Metalama.Extensions.DependencyInjection.

Benefits

  • Reduces boilerplate code: Eliminates the need to manually write constructor parameters and field assignments.
  • Simplifies migration: Eases the transition from attribute-based frameworks to constructor-based ones.
  • Supports multiple frameworks: Works with various dependency injection frameworks (see Injecting dependencies into aspects).

Properties

The [Dependency] aspect provides two properties:

  • IsLazy: Generates code that resolves the dependency lazily, upon the first access.
  • IsRequired: Determines whether the code can execute if the property is missing. If you're using nullable reference types, the IsRequired parameter is inferred from the nullability of the field or property.

Example: Injecting dependencies

The following example demonstrates the code generation pattern for three types of dependencies: required, optional, and lazy.

Source Code
1using Metalama.Extensions.DependencyInjection;
2using Microsoft.Extensions.Hosting;

3using Microsoft.Extensions.Logging;

4
5namespace Doc.DependencyInjectionAspect;
6
7public class DependencyInjectionAspect
8{
9    // This dependency will be optional because the field is nullable.
10    [Dependency]
11    private ILogger? _logger;
12
13    // This dependency will be required because the field is non-nullable.
14    [Dependency]
15    private IHostEnvironment _environment;
16
17    // This dependency will be retrieved on demand.
18    [Dependency( IsLazy = true )]
19    private IHostApplicationLifetime _lifetime;
20
21    public void DoWork()










22    {
23        this._logger?.LogDebug( "Doing some work." );
24
25        if ( !this._environment.IsProduction() )
26        {
27            this._lifetime.StopApplication();
28        }
29    }

30}
Transformed Code
1using System;
2using Metalama.Extensions.DependencyInjection;
3using Metalama.Framework.RunTime;
4using Microsoft.Extensions.Hosting;
5using Microsoft.Extensions.Logging;
6
7namespace Doc.DependencyInjectionAspect;
8
9public class DependencyInjectionAspect
10{
11    // This dependency will be optional because the field is nullable.
12    [Dependency]
13    private ILogger? _logger;
14
15    // This dependency will be required because the field is non-nullable.
16    [Dependency]
17    private IHostEnvironment _environment;
18
19    [Dependency(IsLazy = true)]
20    private IHostApplicationLifetime _lifetime
21    {
22        get
23        {
24            return _lifetimeCache ??= _lifetimeFunc.Invoke();
25        }
26
27        set
28        {
29            throw new NotSupportedException("Cannot set '_lifetime' because of the dependency aspect.");
30        }
31    }
32
33    public void DoWork()
34    {
35        this._logger?.LogDebug("Doing some work.");
36
37        if (!this._environment.IsProduction())
38        {
39            this._lifetime.StopApplication();
40        }
41    }
42
43    private IHostApplicationLifetime? _lifetimeCache;
44    private Func<IHostApplicationLifetime> _lifetimeFunc;
45
46    public DependencyInjectionAspect([AspectGenerated] ILogger<DependencyInjectionAspect> logger = null, [AspectGenerated] IHostEnvironment? environment = null, [AspectGenerated] Func<IHostApplicationLifetime>? lifetime = null)
47    {
48        this._logger = logger; this._environment = environment ?? throw new System.ArgumentNullException(nameof(environment)); this._lifetimeFunc = lifetime ?? throw new System.ArgumentNullException(nameof(lifetime));
49    }
50}

See also