using System.Collections.Immutable;
using System.Text;
using Dpz.Core.SourceGenerator.Models;
namespace Dpz.Core.SourceGenerator;
/// <summary>
/// 根据服务注册元数据生成 Microsoft.Extensions.DependencyInjection 注册源码。
/// </summary>
internal static class ServiceRegistrationSourceBuilder
{
internal static string Generate(ImmutableArray<ServiceRegistration> registrations)
{
var source = new StringBuilder();
source.AppendLine("// <auto-generated />");
source.AppendLine("#nullable enable");
source.AppendLine();
source.AppendLine("namespace Dpz.Core.Service;");
source.AppendLine();
source.AppendLine("internal static partial class GeneratedServiceRegistration");
source.AppendLine("{");
source.AppendLine(
" internal static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddGeneratedRepositoryServices("
);
source.AppendLine(
" this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services"
);
source.AppendLine(" )");
source.AppendLine(" {");
foreach (var registration in registrations)
{
if (registration.Kind == RegistrationKind.HttpClient)
{
AppendHttpClientRegistration(source, registration);
continue;
}
AppendServiceRegistration(source, registration);
}
source.AppendLine();
source.AppendLine(" return services;");
source.AppendLine(" }");
source.AppendLine("}");
return source.ToString();
}
private static void AppendServiceRegistration(
StringBuilder source,
ServiceRegistration registration
)
{
if (registration.RequiresDecorator)
{
AppendLifetimeRegistration(
source,
registration.Lifetime,
registration.ImplementationType
);
AppendLifetimeRegistration(
source,
registration.Lifetime,
registration.ServiceType,
registration.DecoratorType
);
return;
}
AppendLifetimeRegistration(
source,
registration.Lifetime,
registration.ServiceType,
registration.ImplementationType
);
}
private static void AppendHttpClientRegistration(
StringBuilder source,
ServiceRegistration registration
)
{
if (registration.RequiresDecorator)
{
source.Append(
" global::Microsoft.Extensions.DependencyInjection.HttpClientFactoryServiceCollectionExtensions.AddHttpClient<"
);
source.Append(registration.ImplementationType);
AppendHttpClientOptions(source, registration);
source.Append(
" global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions.AddTransient<"
);
source.Append(registration.ServiceType);
source.Append(", ");
source.Append(registration.DecoratorType);
source.AppendLine(">(services);");
return;
}
source.Append(
" global::Microsoft.Extensions.DependencyInjection.HttpClientFactoryServiceCollectionExtensions.AddHttpClient<"
);
source.Append(registration.ServiceType);
source.Append(", ");
source.Append(registration.ImplementationType);
AppendHttpClientOptions(source, registration);
}
private static void AppendLifetimeRegistration(
StringBuilder source,
ServiceLifetime lifetime,
string serviceType
)
{
source.Append(
" global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions."
);
source.Append(GetLifetimeMethodName(lifetime));
source.Append("<");
source.Append(serviceType);
source.AppendLine(">(services);");
}
private static void AppendLifetimeRegistration(
StringBuilder source,
ServiceLifetime lifetime,
string serviceType,
string implementationType
)
{
source.Append(
" global::Microsoft.Extensions.DependencyInjection.ServiceCollectionServiceExtensions."
);
source.Append(GetLifetimeMethodName(lifetime));
source.Append("<");
source.Append(serviceType);
source.Append(", ");
source.Append(implementationType);
source.AppendLine(">(services);");
}
private static void AppendHttpClientOptions(
StringBuilder source,
ServiceRegistration registration
)
{
source.AppendLine(">(services, static (_, client) =>");
source.AppendLine(" {");
source.Append(" client.BaseAddress = new global::System.Uri(");
source.Append(SymbolDisplay.ToCSharpStringLiteral(registration.BaseAddress!));
source.AppendLine(");");
if (registration.TimeoutSeconds > 0)
{
source.Append(" client.Timeout = global::System.TimeSpan.FromSeconds(");
source.Append(registration.TimeoutSeconds);
source.AppendLine(");");
}
source.AppendLine(" });");
}
private static string GetLifetimeMethodName(ServiceLifetime lifetime)
{
return lifetime == ServiceLifetime.Singleton ? "AddSingleton"
: lifetime == ServiceLifetime.Transient ? "AddTransient"
: "AddScoped";
}
}
⚠⚠ 以下内容为AI分析的结果,请根据实际情况进行判断。
代码解释
这是一个 C# 源代码生成器(Source Generator)的组成部分,用于自动生成依赖注入服务注册代码。
主要功能
该类 ServiceRegistrationSourceBuilder 的作用是根据服务注册元数据,自动生成 Microsoft.Extensions.DependencyInjection 框架所需的服务注册代码。
核心方法
1. Generate 方法
- 输入: 服务注册信息的不可变数组
ImmutableArray<ServiceRegistration> - 输出: 生成的 C# 源代码字符串
- 功能:
- 生成一个静态扩展方法
AddGeneratedRepositoryServices - 遍历所有服务注册信息,根据类型分别处理
- 生成一个静态扩展方法
2. AppendServiceRegistration 方法
处理普通服务注册,支持两种场景:
- 带装饰器模式: 先注册实现类型,再注册装饰器
- 普通注册: 直接注册服务接口与实现类型的映射
3. AppendHttpClientRegistration 方法
处理 HttpClient 类型的服务注册:
- 使用
AddHttpClient方法注册 - 配置 BaseAddress(必需)
- 可选配置 Timeout
- 同样支持装饰器模式
4. AppendLifetimeRegistration 方法(两个重载)
- 根据服务生命周期(Singleton/Scoped/Transient)生成对应的注册代码
- 支持只注册实现类型或注册服务接口+实现类型
5. GetLifetimeMethodName 方法
根据生命周期枚举返回对应的 DI 方法名:
Singleton→AddSingletonTransient→AddTransientScoped→AddScoped
生成代码示例
生成的代码结构如下:
// <auto-generated />
#nullable enable
namespace Dpz.Core.Service;
internal static partial class GeneratedServiceRegistration
{
internal static IServiceCollection AddGeneratedRepositoryServices(
this IServiceCollection services)
{
// ... 自动生成的注册代码
return services;
}
}
特点
- 完全限定名称: 使用
global::前缀避免命名冲突 - 装饰器模式支持: 可以为服务添加装饰器层
- HttpClient 专用处理: 特别处理 HTTP 客户端的配置需求
- 类型安全: 在编译时生成强类型的注册代码
这是一个典型的 Roslyn Source Generator 应用场景,可以减少手动编写重复的服务注册代码。
评论加载中...