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
- Flexible Lifetime Management: Easily configure service lifetime via attribute
- Interface-Implementation Decoupling: Explicitly specify service contracts
- Batch Processing: Automatically discover and register all decorated classes
- Code Generation Friendly: Perfect match with T4 templates or Roslyn-based generators
Recommended Improvements
- Add null validation for
ServiceType
- Support automatic interface discovery (e.g.,
IService
-> Service
) - Add duplicate registration detection
- 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
|