ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

ASP.NET Core 构建系统中的 `<Reference>` 引用解析机制:集中化依赖管理与 darc 自动化详解

2026/9/6 19:14:07 拓冰建站 浏览量
ASP.NET Core 构建系统中的 `<Reference>` 引用解析机制:集中化依赖管理与 darc 自动化详解 ASP.NET Core 构建系统中的Reference引用解析机制集中化依赖管理与 darc 自动化详解【免费下载链接】aspnetcoreASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.项目地址: https://gitcode.com/GitHub_Trending/as/aspnetcore在 dotnet/aspnetcore 仓库中绝大多数项目文件使用Reference而非PackageReference或ProjectReference来声明依赖。这不是随意的风格选择而是一套完整的自定义引用解析系统构建系统会根据 servicing维护版与版本更新规则把Reference自动解析为正确的引用类型与版本。本文基于 docs/ReferenceResolution.md 与 eng/targets/ResolveReferences.targets 的实现源码完整讲解这套机制的设计动机、解析流程、关键配置文件以及添加新依赖、新项目和 darc 依赖自动化的可复制操作手册。读完本文你可以独立向该仓库添加新项目和新包依赖并理解构建系统如何强制保证依赖版本的一致性。为什么用Reference设计动机普通 .NET 项目直接用PackageReference IncludeX Version1.2.3 /即可但 ASP.NET Core 作为一个同时产出共享框架shared framework和多个扩展包的大型仓库有三个难以用原生 NuGet 语义满足的需求原文档逐条列出外部依赖版本一致且易查所有外部依赖的版本必须在仓库中集中、可发现地管理而不是散落在几百个项目文件里新版本的包不能引用比上一个发布版本更低的依赖版本即依赖版本只允许单调递增避免升级框架包反而降级某个传递依赖维护版servicing release不得增删现有包中的依赖servicing 构建只能修补已有包的内容不能改变其依赖图。文档还提到一个次要好处这套机制让项目文件更简洁less verbose——你只写一个引用名不用关心它最终解析成项目引用还是 NuGet 包、该用哪个版本。实现这一切的核心是 eng/targets/ResolveReferences.targets。它的文件头注释明确说明more details, see /docs/ReferenceResolution.md即本文档是该实现的官方说明文档。解析实现ResolveReferences.targets源码剖析入口与开关构建系统在属性阶段决定是否为当前项目启用自定义解析。从源码结构看eng/targets/ResolveReferences.targetsPropertyGroup EnableCustomReferenceResolution Condition$(EnableCustomReferenceResolution) AND ($(DotNetBuildSourceOnly) ! true OR $(ExcludeFromSourceOnlyBuild) ! true)true/EnableCustomReferenceResolution ResolveReferencesDependsOn ResolveCustomReferences; $(ResolveReferencesDependsOn); /ResolveReferencesDependsOn /PropertyGroup要点EnableCustomReferenceResolution默认为truesource-build 场景下可被排除也可由项目显式覆盖关键目标ResolveCustomReferences被挂到ResolveReferencesDependsOn前面意味着每次 NuGet restore 和构建都会先执行自定义解析把Reference转换成真正的PackageReference/ProjectReference后再交给标准 SDK 流程。项目可通过三个属性影响解析行为文件头注释属性含义UseLatestPackageReferences是否把Reference解析为 eng/Dependencies.props 中LatestPackageReference的最新版本UseProjectReferences是否优先使用项目引用而非包IsProjectReferenceProvider本项目产出的程序集是否可以作为项目引用提供者被其他项目用Reference间接引用何时用最新版包的四条规则UseLatestPackageReferences的自动推断逻辑ResolveReferences.targets精确编码了文档中servicing 不增删依赖的要求UseLatestPackageReferences Condition $(UseLatestPackageReferences) AND $(IsServicingBuild) ! true true/UseLatestPackageReferences UseLatestPackageReferences Condition $(UseLatestPackageReferences) AND $(IsPackableInNonServicingBuild) ! true true/UseLatestPackageReferences UseLatestPackageReferences Condition $(UseLatestPackageReferences) AND $(IsPackageInThisPatch) true true/UseLatestPackageReferences UseLatestPackageReferences Condition $(UseLatestPackageReferences) false/UseLatestPackageReferences即满足以下任一条件就使用最新依赖版本——当前不是servicing 构建例如准备新的 major/minor 发布项目不是可打包的正常发布项目如测试项目、示例项目该包正在本次补丁中发布新版本外部依赖在补丁中尽量跟随更新。反之若这是 servicing 构建、且项目打包、且包不在本补丁内则解析结果落在false——使用固定版本保证维护版不改变依赖图。而UseProjectReferences默认几乎总是true。解析顺序先项目引用再包第一阶段把Reference转成ProjectReferenceResolveReferences.targetsItemGroup Condition $(EnableCustomReferenceResolution) true AND $(UseProjectReferences) true ProjectReferenceProvider Update(ProjectReference-%(Filename)) DirectUse1 / !-- Find Reference items satisfied using project reference providers. -- Reference Update(ProjectReferenceProvider) ProjectPath%(ProjectReferenceProvider.ProjectPath) / ProjectReference Include(Reference-Distinct()-%(ProjectPath)) / Reference Remove(Reference-HasMetadata(ProjectPath)) / /ItemGroup逻辑是拿所有Reference项与ProjectReferenceProvider项由 eng/ProjectReferences.props 提供映射程序集名 → 产出该程序集的项目文件路径做匹配匹配上的Reference被加上ProjectPath元数据随后物化为真实的ProjectReference并从Reference中移除。注释特别强调Order matters; this comes before package resolution because projects should be used when possible instead of packages——仓库内源码工程优先于外部 NuGet 包。同时有一条边界规则对应当用Reference引用却直接用ProjectReference的提供者项目会被打上DirectUse1标记并在稍后报错见下文。这正是文档中只在测试项目里用ProjectReference这一条建议的强制执行手段。第二阶段把剩余Reference转成PackageReference。核心目标是ResolveCustomReferencesResolveReferences.targets它在CheckForImplicitPackageReferenceOverrides;CollectPackageReferences;ResolvePackageAssets之前运行即跑在 NuGet restore 收集包引用之前。其关键步骤.Sources共享源码包特殊处理凡引用名以.Sources结尾的Reference会被打上IsSharedSourcetrue只消费ContentFiles;Build资产并设置PrivateAssetsAll保证共享源码只参与编译、不进入产物依赖图。版本关联当UseLatestPackageReferencestrue时用 MSBuild 的JoinItems任务把(Reference)与(LatestPackageReference)来自 eng/Dependencies.props做内连接得到带版本的包引用并标记IsImplicitlyDefinedtrue隐式引入区别于显式声明JoinItems Left(Reference) Right(LatestPackageReference) LeftMetadata* RightMetadataVersion Condition $(UseLatestPackageReferences) true Output TaskParameterJoinResult ItemName_LatestPackageReferenceWithVersion / /JoinItems ItemGroup PackageReference Include(_LatestPackageReferenceWithVersion) IsImplicitlyDefinedtrue / Reference Remove(_LatestPackageReferenceWithVersion) / /ItemGroup禁止显式PackageReference除 SDK 隐式定义和带AllowExplicitReferencetrue元数据的项之外任何项目自己写的PackageReference都会触发硬错误Error Condition$(DisablePackageReferenceRestrictions) ! true AND (_ExplicitPackageReference-Count()) ! 0 TextPackageReference items are not allowed. Use lt;Referencegt; instead to replace the reference to (_ExplicitPackageReference, , ). See docs/ReferenceResolution.md for more details. /注意错误信息直接指向本文档——构建系统把docs/ReferenceResolution.md当作面向贡献者的权威指引。未解析引用报错如果一个Reference既找不到项目提供者、也找不到包版本且文件实体不存在则报MSB3245风格的错误Did you update dependencies lists? See docs/ReferenceResolution.md for more details.——这提示你大概率忘了更新 eng/Dependencies.props 或 eng/ProjectReferences.props。元数据误用警告BUILD004警告把%(Private)用在包引用上应改用%(PrivateAssets)BUILD006警告把%(PrivateAssets)用在程序集引用上应改用%(Private)。两者语义不同、不可互换这是新人常见错误。共享框架边界检查_CheckForReferenceBoundaries目标ResolveReferences.targets还负责两条硬性约束共享框架内的项目IsAspNetCoreApptrue若引用了不在共享框架内的程序集直接报错并指向docs/SharedFramework.md框架外的项目不能逐个引用框架内程序集必须以整个Microsoft.AspNetCore.App框架引用为单位任何对项目引用提供者直接使用ProjectReference的行为都会被拒绝错误信息是use a Reference item.Error Condition $(EnableCustomReferenceResolution) true AND (ProjectReferenceProvider-WithMetadataValue(DirectUse, 1)-Count()) ! 0 TextCannot reference quot;%(Identity)quot; with a ProjectReference item; use a Reference item. /关键文件清单文档列出的五个关键文件及其在机制中的角色均可在仓库中直接查看文件作用eng/Dependencies.props仓库中所有可能使用的外部包引用清单以LatestPackageReference Include... /表达是引用解析的输入可被转换成项目中的PackageReference项eng/Versions.props版本属性清单部分可被自动化darc/Maestro更新MSBuild 用它做 restore 与构建eng/Version.Details.xml供自动化更新 eng/Versions.props 中的依赖变量以及 SDK 和msbuild工具集的 global.jsoneng/ProjectReferences.props自动生成的程序集名 → 本地项目映射列出哪些程序集/包可以作为本地项目被引用eng/tools/DependabotDiscovery/DependabotDiscovery.csproj把非 Maestro 管理的包以普通PackageReference重新声明一遍让 Dependabot 能发现并更新它们永不被构建Dependencies.props的版本命名约定eng/Dependencies.props 中每个LatestPackageReference只写包名版本通过一段命名约定 MSBuild 动态属性自动关联eng/Dependencies.propsItemGroup LabelDependencies with versions. !-- Get name prefixes for version properties. -- LatestPackageReference Update(LatestPackageReference) VersionName$([System.String]::new(%(Identity)).Replace(.,))/VersionName /LatestPackageReference !-- Get versions. -- LatestPackageReference Update(LatestPackageReference) Version$(%(VersionName)Version)/Version VersionName / /LatestPackageReference /ItemGroup规则包名去掉所有点后拼接Version后缀即为 eng/Versions.props 中的 MSBuild 属性名。例如包System.Banana对应属性$(SystemBananaVersion)包Microsoft.Extensions.AI对应$(MicrosoftExtensionsAIVersion)可在 eng/Versions.props 中验证如StackExchangeRedisVersion2.7.27/StackExchangeRedisVersion、MessagePackVersion2.5.302/MessagePackVersion。该文件还包含几个值得注意的分层Label.NET team dependenciesdotnet 团队产出的包多数由 Maestro 自动化管理LabelExternal dependencies第三方包AngleSharp、MessagePack、StackExchange.Redis 等由 Dependabot 管理带Version$(XunitV3Version)等显式Version元数据的例外项用于覆盖命名约定特殊 case所有Microsoft.NETCore.App.Runtime.*/Microsoft.NETCore.App.Crossgen2.*的 RID 变体包统一映射到单一属性$(MicrosoftNETCoreAppRefVersion)方便新增 RID。ProjectReferences.props自动生成的项目映射eng/ProjectReferences.props 文件头注明自动生成的运行./eng/scripts/GenerateProjectList.ps1更新。它是一个巨大的ProjectReferenceProvider项列表例如ProjectReferenceProvider IncludeMicrosoft.AspNetCore.Server.Kestrel.Core ProjectPath$(RepoRoot)src\Servers\Kestrel\Core\src\Microsoft.AspNetCore.Server.Kestrel.Core.csproj /这正是文档建议.csproj文件名必须与程序集名一致的原因ProjectReferenceProvider的Include就是程序集名ProjectPath必须能被确定性地推导出来Reference IncludeMicrosoft.AspNetCore.Server.Kestrel.Core /才能匹配上。DependabotDiscovery.csproj给 Dependabot 的影子项目由于本仓库几乎不写PackageReference而 Dependabot 的 NuGet 更新器只能识别字面量PackageReference/PackageVersion于是仓库用了一个巧妙的设计DependabotDiscovery.csproj 把非 Maestro 管理的包原样重新声明为普通PackageReference引用同一套$(...Version)属性。Dependabot 在这个影子项目里 bump 版本属性后同一属性经由 eng/Versions.props 流入所有真实项目。DependabotDiscovery/README.md 进一步说明了准入规则必须同时满足未被 Maestro/IdentityModel 管理、不是ProjectReferenceProvider名、在eng/Versions.props有真实的版本属性、可被 Dependabot 报告为顶层可更新依赖项目同时 targetnet472与$(DefaultNetCoreTargetFramework)以覆盖只支持单一 TFM 的包如Microsoft.Owin.*只有 net45 资产并通过NoWarn抑制 NU1605 降级冲突——因为它永远不会真正构建。编写.csproj的推荐清单文档给出的规则配合上文源码可看到每条都有强制执行机制用Reference——解析器唯一支持的自定义依赖声明方式不要用PackageReference——ResolveCustomReferences会直接报错需要新包时先加到 eng/Dependencies.props 和 eng/Versions.props若包来自 partner 团队且需自动更新版本还要在 eng/Version.Details.xml 添加条目否则无 Maestro 自动化把包加进 DependabotDiscovery.csproj 让 Dependabot 能发现并更新它详见其 README只在测试项目中用ProjectReference——边界检查目标会拒绝其他用法.csproj文件名与程序集名保持一致新增项目后运行eng/scripts/GenerateProjectList.ps1或build.cmd /t:GenerateProjectList重新生成项目映射。GenerateProjectList.ps1的实现eng/scripts/GenerateProjectList.ps1只是薄封装它调用eng/common/msbuild.ps1对 eng/CodeGen.proj 执行GenerateProjectList目标该目标对每个项目调用GetReferencesProvided在 ResolveReferences.targets 中定义递归收集每个 TFM 下项目提供的程序集最终写出ProjectReferences.props等生成文件。示例一向仓库添加新项目文档给出的三步流程创建.csproj文件名与程序集名一致用Reference声明依赖运行eng/scripts/GenerateProjectList.ps1重新生成 eng/ProjectReferences.props把新项目加入 AspNetCore.slnx 及相关的*.slnf文件。若项目希望被其他项目用Reference间接引用还需在项目中设置IsProjectReferenceProvidertrue——_GetReferencesProvided目标据此把程序集名写入ProvidesReference项成为项目列表生成器的输入。示例二添加新的包依赖文档以添加System.Banana为例给出完整步骤这里保留原步骤并结合源码补齐细节。第 1 步在 .csproj 中写Reference IncludeSystem.Banana /第 2 步在 eng/Dependencies.props 添加LatestPackageReference IncludeSystem.Banana /第 3 步二选一根据包的来源选择自动化路径路径 A来自其他 dotnet 团队由 Maestro 机器人自动更新在 eng/Versions.props 添加版本属性按命名约定System.Banana对应SystemBananaVersionSystemBananaVersion0.0.1-beta-1/SystemBananaVersion在 eng/Version.Details.xml 的ProductDependencies中添加依赖条目ProductDependencies !-- ... -- Dependency NameSystem.Banana Version0.0.1-beta-1 Urihttps://github.com/dotnet/corefx/Uri Sha000000/Sha /Dependency !-- ... -- /ProductDependencies若不知道 0.0.1-beta-1 对应的源码提交哈希可以用000000占位机器人下次运行时会自动修正。当前仓库中 eng/Version.Details.xml 的实际条目都带有真实的Uri、Sha甚至BarId/SourceBuildTarball元数据可作为格式参照。若依赖来自 dotnet/runtime 且你在更新 dotnet/aspnetcore-tooling需要给Dependency元素加CoherentParentDependency属性Dependency NameSystem.Banana Version0.0.1-beta-1 CoherentParentDependencyMicrosoft.CodeAnalysis.Razor !-- ... -- /Dependency其含义System.Banana应采用的 dotnet/runtime 依赖版本基于产出所选Microsoft.CodeAnalysis.Razor的那个 dotnet/aspnetcore 构建来确定——即 dotnet/runtime 与 dotnet/aspnetcore 的依赖必须相干coherent避免两个仓库的包混用不一致的 runtime 版本。按文档说明在 dotnet/aspnetcore-tooling 中该属性值应为Microsoft.CodeAnalysis.Razor。路径 B无 Maestro 自动化交给 Dependabot在 DependabotDiscovery.csproj 中添加PackageReference IncludeSystem.Banana Version$(SystemBananaVersion) /注意 CI 会强制同步eng/scripts/CodeCheck.ps1 会比对eng/Dependencies.props变更是否伴随DependabotDiscovery.csproj的对应更新缺失则构建失败脚本中明确检查 eng/Dependencies.props changed but ... was not updated。darc 速查手册依赖自动化操作darc是 dotnet 生态仓库间依赖管理的命令行工具。文档给出了完整速查表安装方式是运行eng/common目录下的darc-init脚本仓库中存在 eng/common/darc-init.ps1 与 eng/common/darc-init.sh安装后需按官方 Darc 文档配置相应的访问令牌。文档同时提示以下大部分功能现在也可通过 Maestro Web UI 完成推荐优先尝试 UI。查看仓库的订阅列表订阅subscription定义了监听哪些生态仓库的更新、更新频率等元数据darc get-subscriptions --target-branch main --target-repo aspnetcore$ --regex启用 / 禁用订阅darc subscription-status --id {subscriptionIdHere} --enable darc subscription-status --id {subscriptionIdHere} --disable触发订阅触发订阅会搜索其依赖的更新并通过 dotnet-maestro 机器人在目标仓库开一个 PR 带入这些变更darc trigger-subscriptions --id {subscriptionIdHere}手动更新依赖若 dotnet-maestro 机器人未正确更新依赖可用darc update-dependencies手动完成。注意需在独立分支执行并提 PR。这些工作本来由机器人在订阅触发时自动完成例如订阅频率为EveryBuild时依赖方构建完成后约 15 分钟darc update-dependencies --channel .NET Core 3.1 Release darc update-dependencies --channel .NET 5 Dev --source-repo efcore文档建议优先用trigger-subscriptions创建依赖更新而不是在自己的 PR 里手动更新依赖。切换订阅的批量batchable行为订阅可以批处理检测到依赖更新时darc会把该更新的提交与已有的依赖 PR 合并捆绑。切换批量行为需使用update-subscription命令darc update-subscription --id {subscriptionIdHere}系统默认编辑器会打开允许编辑订阅元数据。禁用批量将Batchable设为False并把Merge Policies部分设为- Name: Standard Properties: {}启用批量将Batchable设为True并移除订阅上已设置的Merge Policies。注意Merge policies 只能设置在非批量订阅上切换 batchability 时必须正确设置/取消Merge Policies字段。小结机制全貌把文档与源码串起来看整套系统的闭环是项目只写Reference IncludeX /ResolveReferences.targets 在 restore 前依次尝试匹配 eng/ProjectReferences.props → 转ProjectReference再匹配 eng/Dependencies.props 的LatestPackageReference→ 转PackageReference版本取自 eng/Versions.props 的命名约定属性解析不到项目也不报错的项目最终报Did you update dependencies lists?错误并指向本文档版本由两条自动化管道维护dotnet 团队间用 Maestro/darceng/Version.Details.xml darc命令第三方包用 DependabotDependabotDiscovery.csproj 影子项目CI 层由 CodeCheck.ps1 强制Dependencies.props与影子项目同步由 MSBuild 错误强制禁止显式 PackageReference禁止绕过 Reference 引用提供者等规则。这套设计的净效果是贡献者只需理解引用名这一个概念而版本一致性、servicing 边界、依赖图稳定性全部由构建系统集中强制执行——这正是文档开篇所说without requiring most ASP.NET Core contributors to understand the complex rules for how versions and references should work的实现方式。【免费下载链接】aspnetcoreASP.NET Core is a cross-platform .NET framework for building modern cloud-based web applications on Windows, Mac, or Linux.项目地址: https://gitcode.com/GitHub_Trending/as/aspnetcore创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考