Open sandboxFocusImprove this doc

Metalama.Extensions.DependencyInjection

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 does not rely on custom attributes on fields and properties. Instead, it requires you to add the dependencies to the class constructor and store them as fields. This requirement can lead to some boilerplate code, especially if there are numerous dependencies in a complex class hierarchy. Moreover, it can be tedious to migrate your code from an attribute-based framework to a constructor-based one.

To alleviate these minor inconveniences, you can employ the [Dependency] aspect of Metalama.Extensions.DependencyInjection.

The advantages of this aspect include:

  • Reduction of boilerplate code,
  • Simplified migration from attribute-based frameworks to constructor-based ones,
  • Compatibility with multiple dependency injection frameworks (see Injecting dependencies into aspects).

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 are 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 dependency: required, optional, and lazy.

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

3using Microsoft.Extensions.Logging;
4
5#pragma warning disable CS0169 // Field is never used
6#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
7#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value
8
9namespace Doc.DependencyInjectionAspect;
10
11public class DependencyInjectionAspect
12{
13    // This dependency will be optional because the field is nullable.
14    [Dependency]
15    private ILogger? _logger;
16
17    // This dependency will be required because the field is non-nullable.
18    [Dependency]
19    private IHostEnvironment _environment;
20
21    // This dependency will be retrieved on demand.








22    [Dependency( IsLazy = true )]
23    private IHostApplicationLifetime _lifetime;
24
25    public void DoWork()


26    {
27        this._logger?.LogDebug( "Doing some work." );
28
29        if ( !this._environment.IsProduction() )
30        {
31            this._lifetime.StopApplication();
32        }
33    }

34}
Transformed Code
1using System;
2using Metalama.Extensions.DependencyInjection;
3using Microsoft.Extensions.Hosting;
4using Microsoft.Extensions.Logging;
5
6#pragma warning disable CS0169 // Field is never used
7#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider declaring as nullable.
8#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value
9
10namespace Doc.DependencyInjectionAspect;
11
12public class DependencyInjectionAspect
13{
14    // This dependency will be optional because the field is nullable.
15    [Dependency]
16    private ILogger? _logger;
17
18    // This dependency will be required because the field is non-nullable.
19    [Dependency]
20    private IHostEnvironment _environment;
21
22    [Dependency(IsLazy = true)]
23    private IHostApplicationLifetime _lifetime
24    {
25        get
26        {
27            return _lifetimeCache ??= _lifetimeFunc.Invoke();
28        }
29
30        set
31        {
32            throw new NotSupportedException("Cannot set '_lifetime' because of the dependency aspect.");
33        }
34    }
35
36    public void DoWork()
37    {
38        this._logger?.LogDebug("Doing some work.");
39
40        if (!this._environment.IsProduction())
41        {
42            this._lifetime.StopApplication();
43        }
44    }
45
46    private IHostApplicationLifetime? _lifetimeCache;
47    private Func<IHostApplicationLifetime> _lifetimeFunc;
48
49    public DependencyInjectionAspect(ILogger<DependencyInjectionAspect> logger = default, IHostEnvironment? environment = default, Func<IHostApplicationLifetime>? lifetime = default)
50    {
51        this._logger = logger; this._environment = environment ?? throw new System.ArgumentNullException(nameof(environment)); this._lifetimeFunc = lifetime ?? throw new System.ArgumentNullException(nameof(lifetime));
52    }
53}