还在为ASP.NET Core的DI容器性能担忧吗?每次服务解析都要经历复杂的反射调用,在高并发场景下成为性能瓶颈。今天我们来探索一个"黑科技"——Source Generator编译时DI容器,让服务注册在编译期完成,运行时直接调用,性能提升高达10倍!
想象一下,不再需要手写冗长的services.AddScoped<IUserService, UserService>()代码,只需要在类上加个[Scoped]特性,编译器就自动帮你生成所有注册代码。这不是梦想,而是.NET 5+时代的现实!
传统的DI容器在运行时需要:
C#// 运行时反射解析
var userService = serviceProvider.GetRequiredService<IUserService>();
// 内部执行大量反射操作:
// 1. 查找服务类型映射
// 2. 反射创建实例
// 3. 递归解析依赖
// 4. 调用构造函数
这个过程涉及:
Activator.CreateInstance或表达式树在高频调用场景下,这些开销积少成多,成为性能瓶颈。
Source Generator是.NET 5引入的编译时代码生成技术,可以:


首先创建AppDIAttributes项目,定义标记特性:
C#// AppDIAttributes/Attributes.cs
namespace AppDIAttributes
{
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class SingletonAttribute : Attribute
{
public Type ServiceType { get; }
public SingletonAttribute(Type serviceType = null)
{
ServiceType = serviceType;
}
}
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class ScopedAttribute : Attribute
{
public Type ServiceType { get; }
public ScopedAttribute(Type serviceType = null)
{
ServiceType = serviceType;
}
}
// TransientAttribute和ServiceAttribute类似...
public enum ServiceLifetime
{
Transient,
Scoped,
Singleton
}
}
核心的代码生成逻辑:
C#// AppDISourceGenerator/DIContainerGenerator.cs
[Generator]
public class DIContainerGenerator : ISourceGenerator
{
public void Execute(GeneratorExecutionContext context)
{
var receiver = context.SyntaxContextReceiver as DIServiceSyntaxReceiver;
var services = new List<ServiceRegistration>();
// 分析所有标记了DI特性的类
foreach (var classDeclaration in receiver.CandidateClasses)
{
var model = context.Compilation.GetSemanticModel(classDeclaration.SyntaxTree);
var classSymbol = model.GetDeclaredSymbol(classDeclaration);
var registration = AnalyzeServiceClass(classSymbol);
if (registration != null)
services.Add(registration);
}
// 生成扩展方法
var sourceCode = GenerateContainerRegistrationCode(services);
context.AddSource("DIContainer.g.cs", sourceCode);
}
private string GenerateContainerRegistrationCode(List<ServiceRegistration> services)
{
var sb = new StringBuilder();
sb.AppendLine("namespace GeneratedDI");
sb.AppendLine("{");
sb.AppendLine(" public static class DIContainerExtensions");
sb.AppendLine(" {");
sb.AppendLine(" public static IServiceCollection AddGeneratedServices(this IServiceCollection services)");
sb.AppendLine(" {");
foreach (var service in services)
{
switch (service.Lifetime)
{
case ServiceLifetime.Singleton:
sb.AppendLine($" services.AddSingleton<{service.ServiceTypeName}, {service.ImplementationTypeName}>();");
break;
case ServiceLifetime.Scoped:
sb.AppendLine($" services.AddScoped<{service.ServiceTypeName}, {service.ImplementationTypeName}>();");
break;
// ... 其他生命周期
}
}
sb.AppendLine(" return services;");
sb.AppendLine(" }");
sb.AppendLine(" }");
sb.AppendLine("}");
return sb.ToString();
}
}
⚠️ 关键配置提醒:Source Generator项目文件必须添加:
XML<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<IncludeBuildOutput>false</IncludeBuildOutput>
<GeneratePackageOnBuild>false</GeneratePackageOnBuild>
<DevelopmentDependency>true</DevelopmentDependency>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.5.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\AppDIAttributes\AppDIAttributes.csproj" />
</ItemGroup>
</Project>
在业务类上添加特性标记:
C#// 用户服务 - 作用域生命周期
[Scoped(typeof(IUserService))]
public class UserService : IUserService
{
private readonly IEmailService _emailService;
private readonly ILoggerService _logger;
public UserService(IEmailService emailService, ILoggerService logger)
{
_emailService = emailService;
_logger = logger;
}
public async Task<User> GetUserAsync(int id)
{
_logger.LogInfo($"Getting user with ID: {id}");
// 业务逻辑...
}
}
// 邮件服务 - 单例生命周期
[Singleton(typeof(IEmailService))]
public class EmailService : IEmailService
{
public async Task SendEmailAsync(string to, string subject, string body)
{
// 邮件发送逻辑...
}
}
编译后自动生成扩展方法,一行代码完成所有服务注册:
C#// Program.cs
var hostBuilder = Host.CreateDefaultBuilder(args)
.ConfigureServices((context, services) =>
{
// 🎉 一行代码注册所有服务!
services.AddGeneratedServices();
});

实测数据对比(10万次服务解析):
C#// 性能测试代码
const int iterations = 100_000;
// 编译时DI性能测试
var sw = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++)
{
using var scope = services.CreateScope();
var userService = scope.ServiceProvider.GetRequiredService<IUserService>();
var emailService = scope.ServiceProvider.GetRequiredService<IEmailService>();
}
sw.Stop();
Console.WriteLine($"编译期 DI 解析: {sw.ElapsedMilliseconds} ms");
Console.WriteLine($"平均每次: {(double)sw.ElapsedTicks / iterations:F4} ticks");
测试结果对比:
C#[Scoped]
public class Repository<T> : IRepository<T> where T : class
{
// 泛型仓储实现
}
// 生成的注册代码
services.AddScoped(typeof(IRepository<>), typeof(Repository<>));
C#[Singleton(typeof(IConfigService))]
[ConditionalRegistration("Development")]
public class DevConfigService : IConfigService
{
// 开发环境专用配置服务
}
C#// 无需指定ServiceType,自动推断第一个接口
[Scoped] // 自动推断为IUserService
public class UserService : IUserService, IDisposable
{
}
Source Generator可以在编译期检测循环依赖:
C#// 编译期错误提示
// Error: Circular dependency detected: UserService -> EmailService -> UserService
错误做法:跨程序集的服务无法自动发现
正确做法:使用部分类或配置文件指定扫描程序集
Source Generator项目必须这样引用:
XML<ProjectReference Include="..\AppDISourceGenerator\AppDISourceGenerator.csproj"
OutputItemType="Analyzer"
ReferenceOutputAssembly="false" />
编译后在此路径查看生成的代码:
textobj/Debug/net8.0/generated/AppDISourceGenerator/
C#context.ReportDiagnostic(Diagnostic.Create(
new DiagnosticDescriptor(
"DI001",
"Service Registered",
$"注册服务: {serviceType} -> {implementationType}",
"DI",
DiagnosticSeverity.Info,
true
),
Location.None
));
Source Generator代表了.NET技术的发展趋势——将运行时工作前移到编译期,既提升了性能,又保持了代码的简洁性。在追求极致性能的企业级应用中,这种技术将大有作为。
💭 讨论时间:
🔖 收藏价值:完整的编译时DI容器实现,可直接应用于生产项目
觉得这个技术方案有价值?请转发给更多.NET同行,让大家一起享受编译时优化带来的性能红利!
相关信息
通过网盘分享的文件:AppDISourceGenerator.zip 链接: https://pan.baidu.com/s/1cevYkOzBPKrD644tK-tsjA?pwd=r9v5 提取码: r9v5 --来自百度网盘超级会员v9的分享
本文作者:技术老小子
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!