新闻详情 资讯动态

全面了解最新资讯与建站知识,洞察行业趋势。

行业资讯

CleanArchitecture 模板 ADR-001:用 .NET 内置 DI 替换 Autofac 的决策记录与源码级落地

发布时间:2026/9/13 11:01:23
CleanArchitecture 模板 ADR-001:用 .NET 内置 DI 替换 Autofac 的决策记录与源码级落地 CleanArchitecture 模板 ADR-001用 .NET 内置 DI 替换 Autofac 的决策记录与源码级落地【免费下载链接】CleanArchitectureClean Architecture Solution Template: A proven Clean Architecture Template for ASP.NET Core 10项目地址: https://gitcode.com/GitHub_Trending/cl/CleanArchitecture本文以 CleanArchitecture 解决方案模板Clean Architecture Solution Template面向 ASP.NET Core 10 的 .NET 模板中第一份正式的架构决策记录 ADR 001: Replace Autofac with .NET DI 为主体完整继承其 Status、Context、Decision、Consequences 四段内容并结合模板中真实的 DI 注册代码组合根、分层扩展方法、Mediator 源生成器配置逐条印证该决策在代码库中的落点帮助读者掌握在 Clean Architecture 分层项目中仅用 .NET 内置 DI 完成全部依赖注册的完整做法。决策记录ADR 001的核心内容ADR 001 是该模板docs/content/architecture-decisions/目录下的第一份架构决策记录目录索引见 architecture-decisions/_index.md。其结构与结论如下Status状态Accepted已接受。Decision决策基于社区反馈和对项目需求的审视模板决定移除 Autofac全面转向 .NET 内置依赖注入基础设施。目标是简化代码库、减少外部依赖。Consequences后果/收益原文列出三点Simplified Codebase简化代码库移除 Autofac 后代码库更干净、更易维护且遵循 .NET 标准Reduced Complexity降低复杂度过渡消除了 Autofac 专属的额外文件与配置新贡献者更容易理解项目Standardization标准化采用 .NET 内置 DI 与其他 .NET 项目保持一致熟悉框架的开发者可以更顺畅地参与贡献。背景Context为什么当初用 Autofac又为什么换掉ADR 文档完整保留了这段决策的历史脉络这也是理解本决策最重要的部分当初采用 Autofac 的原因在其被引入的年代支持 decorator装饰器和 module模块等高级场景且这些扩展代码可以就近放在对应实现旁边稳定性与成熟度经过验证——在 .NET Core 内置 DI 功能完善之前它已在大量项目中使用。后来切换的原因随着 .NET 生态演进内置 DI 容器开始满足项目需求不再需要引入 Autofac 带来的额外复杂度。文档指出 .NET DI 框架已显著成熟对典型场景提供了足够的能力包括通过扩展方法改进的配置支持Improved support for configuration through extension methods对熟悉 .NET 惯例的新贡献者而言更平缓的学习曲线。原文档还在 References 一节引用了触发本次决策的社区讨论Issue #649主题即为什么这个仓库用 Autofac 而不是 .NET 自带的 DI 基础设施以及一篇关于 ADR 最佳实践的外部文章。本文不展开外部链接但建议把决策由真实社区问题驱动这一点视为 ADR 写作的示范先有问题再谈决策。决策落地一组合根只做编排不做具体注册决策本身是一句话去掉 Autofac用内置 DI真正能检验它的是模板里所有 DI 注册代码的写法。组合根入口是 Web 项目 Program.csvar builder WebApplication.CreateBuilder(args); builder.AddServiceDefaults() // This sets up OpenTelemetry logging .AddLoggerConfigs(); // This adds Serilog for console formatting using var loggerFactory LoggerFactory.Create(config config.AddConsole()); var startupLogger loggerFactory.CreateLoggerProgram(); startupLogger.LogInformation(Starting web host); builder.Services.AddOptionConfigs(builder.Configuration, startupLogger, builder); builder.Services.AddServiceConfigs(startupLogger, builder); builder.Services.AddFastEndpoints() .SwaggerDocument(/* ... v1 文档配置 ... */); var app builder.Build(); await app.UseAppMiddlewareAndSeedDatabase(); app.MapDefaultEndpoints(); // Aspire health checks and metrics app.Run();值得对照 ADR 意图观察的三个细节没有任何ContainerBuilder、Populate()、UseProvider()等 Autofac 痕迹——整个启动流程只围绕builder.Services即IServiceCollection展开这正是 ADR 消除 Autofac 专属额外文件与配置一条的直接体现组合根只调用三个扩展方法AddOptionConfigs绑定 Options、AddServiceConfigs注册业务服务与AddFastEndpointsAPI 层。注册细节全部下沉到 Configurations 目录下的扩展方法中符合 ADR 中简化、标准化的表述文件末尾特意保留public partial class Program { }注释说明是为了让集成测试引用正确的程序集来构建宿主——内置 DI 配合WebApplicationFactory的测试集成方式同样受益于此。决策落地二Options 模式替代 Autofac 模块的配置能力OptionConfigs.cs 展示了 ADR Context 中提到的通过扩展方法改进的配置支持在模板中的具体形态public static IServiceCollection AddOptionConfigs(this IServiceCollection services, IConfiguration configuration, Microsoft.Extensions.Logging.ILogger logger, WebApplicationBuilder builder) { services.ConfigureMailserverConfiguration(configuration.GetSection(Mailserver)) // Configure Web Behavior .ConfigureCookiePolicyOptions(options { options.CheckConsentNeeded context true; options.MinimumSameSitePolicy SameSiteMode.None; }); if (builder.Environment.IsDevelopment()) { // 仅开发环境注册 ListStartupServices用于诊断已注册的服务 services.ConfigureServiceConfig(config { config.Services new ListServiceDescriptor(builder.Services); config.Path /listservices; }); } logger.LogInformation({Project} were configured, Options); return services; }几个与 ADR 主题强相关的点services.ConfigureT(section)就是 .NET 内置 DI 的Options 模式——用一行代码把配置节绑定为类型化对象并注册到容器消费端通过IOptionsT解析。这正是 ADR 所说的extension methods 改进配置支持的实际例子替代了过去靠 Autofac Module 承载配置的职责builder.Environment.IsDevelopment()的条件注册只在开发环境挂载Ardalis.ListStartupServices的/listservices诊断端点是内置 DI 完全原生支持的写法。这为读者提供了一个可验证手段在开发环境运行 Web 项目后访问/listservices即可看到全部已注册服务清单直接检验仅靠内置 DI 是否注册齐全。决策落地三Infrastructure 层的注册与依赖倒置InfrastructureServiceExtensions.cs 是 ADR 收益减少外部依赖最能被验证的地方——全部注册均使用内置IServiceCollectionAPIpublic static IServiceCollection AddInfrastructureServices( this IServiceCollection services, ConfigurationManager config, ILogger logger) { // 连接串按优先级解析 // 1. cleanarchitecture - Aspire 通过 .WithReference(cleanArchDb) 提供 // 2. DefaultConnection - SQL Server默认仅 Windows可用 USE_SQL_SERVERtrue 强制 // 3. SqliteConnection - 回退到 SQLite bool isWindows OperatingSystem.IsWindows(); bool forceSqlServer Environment.GetEnvironmentVariable(USE_SQL_SERVER) true; string? connectionString config.GetConnectionString(cleanarchitecture) ?? ((isWindows || forceSqlServer) ? config.GetConnectionString(DefaultConnection) : null) ?? config.GetConnectionString(SqliteConnection); Guard.Against.Null(connectionString); services.AddScopedEventDispatchInterceptor(); services.AddScopedIDomainEventDispatcher, MediatorDomainEventDispatcher(); services.AddDbContextAppDbContext((provider, options) { var eventDispatchInterceptor provider.GetRequiredServiceEventDispatchInterceptor(); // Aspire 或 Windows/强制下的 DefaultConnection 用 SQL Server否则 SQLite options.UseSqlServer(connectionString); // 或 options.UseSqlite(connectionString) options.AddInterceptors(eventDispatchInterceptor); }); services.AddScoped(typeof(IRepository), typeof(EfRepository)) .AddScoped(typeof(IReadRepository), typeof(EfRepository)) .AddScopedIListContributorsQueryService, ListContributorsQueryService() .AddScopedIDeleteContributorService, DeleteContributorService(); return services; }从源码结构看这段代码集中演示了内置 DI 覆盖 Autofac 时代常见诉求的三种手法开放泛型注册AddScoped(typeof(IRepository), typeof(EfRepository))一条语句完成接口→实现的泛型映射无需 Autofac 的泛型委托配置工厂函数中按需解析协作对象AddDbContext的注册委托接收ServiceProvider provider参数在其中GetRequiredServiceEventDispatchInterceptor()拿到拦截器再options.AddInterceptors(...)挂到 EF Core 上。这种注册时依赖另一个已注册服务的场景过去常被列举为 Autofac 的卖点此处表明内置 DI 通过工厂委托同样可以表达Core 层接口 Infrastructure 层实现的倒置IListContributorsQueryService、IDeleteContributorService等接口定义在 Core 项目实现在 Infrastructure 项目并在此注册——Clean Architecture 的依赖方向不因 DI 容器的选择而改变。决策落地四Mediator 源生成器同样只依赖 IServiceCollection模板的 UseCases 层使用 Mediator 源生成器替代 MediatR背景可见 v10→v11 迁移指南中MediatR Replacement一节。其注册在 MediatorConfig.cs 中依旧完全基于内置 DIservices.AddMediator(options { // Lifetime: Singleton 最快见文档也支持 Scoped/Transient options.ServiceLifetime ServiceLifetime.Scoped; // 每个待扫描程序集提供任一类型即可生成器从类型定位程序集 options.Assemblies [ typeof(Contributor), // Core typeof(CreateContributorCommand), // UseCases typeof(InfrastructureServiceExtensions), // Infrastructure typeof(MediatorConfig) // Web ]; // 流水线 Behavior顺序敏感 options.PipelineBehaviors [ typeof(LoggingBehavior,) ]; }); // 备选不走 AOT 时也可自行通过 DI 注册 Behavior // services.AddScoped(typeof(IPipelineBehavior,), typeof(LoggingBehavior,));这里的options.ServiceLifetime ServiceLifetime.Scoped直接复用了 .NET DI 的ServiceLifetime枚举作为 Mediator 的生命周期开关注释中列出的两种备选注册方式AddScoped(typeof(IPipelineBehavior,), ...)也都是标准内置 DI API。可以说Mediator 换成源生成器 内置 DI与移除 Autofac是同一方向上的两次依赖精简二者共同构成了当前模板的 DI 全貌。环境相关的条件注册ServiceConfigs 的组合方式ServiceConfigs.cs 把上述各块串起来并展示了环境区分注册开发环境用本地邮件服务器生产用MimeKitEmailSenderpublic static IServiceCollection AddServiceConfigs(this IServiceCollection services, Microsoft.Extensions.Logging.ILogger logger, WebApplicationBuilder builder) { services.AddInfrastructureServices(builder.Configuration, logger) .AddMediatorSourceGen(logger); if (builder.Environment.IsDevelopment()) { // 使用本地测试邮件服务器由 Aspire 配置 services.AddScopedIEmailSender, MimeKitEmailSender(); // 否则使用services.AddScopedIEmailSender, FakeEmailSender(); } else { services.AddScopedIEmailSender, MimeKitEmailSender(); } logger.LogInformation({Project} services registered, Mediator Source Generator and Email Sender); return services; }调用顺序体现了清晰的分层Web 层的AddServiceConfigs→ Infrastructure 层的AddInfrastructureServices→ Mediator 配置每步返回IServiceCollection形成链式注册且每一步都通过logger.LogInformation记录谁注册了什么便于排查。如何复现与验证生成一个使用本模板的新解决方案v11 模板按 v10-to-v11 迁移指南所述流程可用dotnet new clean-arch -o Your.ProjectName创建新工程再与旧工程做差异比对检查新生成的项目中是否存在 Autofac 相关包引用Directory.Packages.props中不应再有 Autofac 条目、组合根中是否只有builder.Services相关调用以开发环境运行 Web 项目访问/listservices端点由 OptionConfigs.cs 中Ardalis.ListStartupServices提供逐项核对服务注册清单。小结ADR 001 的核心判断是当 .NET 内置 DI 已经能覆盖 Options 绑定、条件注册、开放泛型注册、工厂委托内按需解析等场景时Autofac 带来的复杂度不再物有所值。从源码结构看模板中 Program.cs、OptionConfigs.cs、ServiceConfigs.cs、MediatorConfig.cs 与 InfrastructureServiceExtensions.cs 构成了完整的对照样本所有注册均出自IServiceCollection标准 API没有第三方容器的痕迹。对读者而言这份 ADR 的价值不仅是换了一个 DI 容器更示范了一个可操作的判断框架——当内置能力与外部库的功能差距缩小到小于维护成本时就应以 ADR 形式记录并执行去依赖化决策。【免费下载链接】CleanArchitectureClean Architecture Solution Template: A proven Clean Architecture Template for ASP.NET Core 10项目地址: https://gitcode.com/GitHub_Trending/cl/CleanArchitecture创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

想做一个「会获客」的企业网站?

留下需求,1 小时内获取专属建站方案与透明报价。

免费咨询方案