Featured image of post Implement Batch Dependency Injection in .NET 6 WebAPI

Implement Batch Dependency Injection in .NET 6 WebAPI

dependency-injection

Preface

In .NET WebAPI development, when dealing with numerous business modules and classes that require dependency injection, manually registering each service becomes tedious and harms code readability. This article demonstrates an elegant batch injection solution using custom attributes.

Implementation

1. Create Custom Attribute

Create an AppServiceAttribute.cs file:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
public class AppServiceAttribute : Attribute
{
    public ServiceLifeType ServiceLifeType { get; set; } = ServiceLifeType.Singleton;
    public Type ServiceType { get; set; }
}

public enum ServiceLifeType
{
    Transient,
    Scoped,
    Singleton
}

2. Batch Registration Extension

Create AppServiceExtensions.cs:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public static class AppServiceExtensions
{
    public static void AddAppServices(this IServiceCollection services, params string[] assemblyNames)
    {
        foreach (var name in assemblyNames)
        {
            var assembly = Assembly.Load(name);
            foreach (var type in assembly.GetTypes())
            {
                var attribute = type.GetCustomAttribute<AppServiceAttribute>();
                if (attribute != null)
                {
                    RegisterService(services, type, attribute);
                }
            }
        }
    }

    private static void RegisterService(IServiceCollection services, Type implementationType, AppServiceAttribute attribute)
    {
        var serviceType = attribute.ServiceType ?? implementationType;

        switch (attribute.ServiceLifeType)
        {
            case ServiceLifeType.Singleton:
                services.AddSingleton(serviceType, implementationType);
                break;
            case ServiceLifeType.Scoped:
                services.AddScoped(serviceType, implementationType);
                break;
            case ServiceLifeType.Transient:
                services.AddTransient(serviceType, implementationType);
                break;
            default:
                services.AddTransient(serviceType, implementationType);
                break;
        }
    }
}

3. Service Implementation

ISystemService.cs:

1
2
3
4
public interface ISystemService
{
    int Add(int a, int b);
}

SystemService.cs:

1
2
3
4
5
[AppService(ServiceType = typeof(ISystemService), ServiceLifeType = ServiceLifeType.Scoped)]
public class SystemService : ISystemService
{
    public int Add(int a, int b) => a + b;
}

4. Registration in Program.cs

1
2
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAppServices("Your.Service.Assembly");

Key Features

  1. Flexible Lifetime Management: Easily configure service lifetime via attribute
  2. Interface-Implementation Decoupling: Explicitly specify service contracts
  3. Batch Processing: Automatically discover and register all decorated classes
  4. Code Generation Friendly: Perfect match with T4 templates or Roslyn-based generators
  1. Add null validation for ServiceType
  2. Support automatic interface discovery (e.g., IService -> Service)
  3. Add duplicate registration detection
  4. Implement assembly scanning with IAssemblyProvider

Why Not Just Use Manual Registration?
While this approach still requires attributes, it becomes extremely powerful when combined with code generators - most boilerplate code can be automatically generated during build process.

1
2
3
4
5
6

This implementation provides:
✅ Clean architecture separation
✅ Centralized dependency management
✅ Improved code maintainability
✅ Reduced human error in registration
comments powered by Disqus