自定义FolderNuGetProject调用InstallPackageAsync无法安装文件(NuGet v6.3)
NuGet v6.3自定义FolderNuGetProject安装包空引用问题解决
问题概述
开发一款支持NuGet插件下载与动态加载的应用,基于FolderNuGetProject自定义了PluginProject类。升级到NuGet.Packaging和NuGet.PackageManagement v6.3版本后,出现以下问题:
- 重写的
InstallPackageAsync方法调用base.InstallPackageAsync时抛出空引用异常,v4.0版本无此问题; - 若不调用
base.InstallPackageAsync,目标目录仅生成packages.config文件,无实际安装的包文件; - 直接使用原生
FolderNuGetProject也会触发相同异常,排查发现nuGetProjectContext.PackageExtractionContext为null。
原因分析
- API行为变更:NuGet从v4到v6的版本迭代中,
PackageExtractionContext不再由框架自动初始化。FolderNuGetProject的InstallPackageAsync方法依赖该上下文完成包的解压与安装,而EmptyNuGetProjectContext默认不会填充此属性,导致空引用。 - 逻辑冲突:自定义
PluginProject同时混用FolderNuGetProject(负责packages目录的包文件管理)与PackagesConfigNuGetProject(负责packages.config配置)的逻辑,且原安装方法中存在明显逻辑错误(安装流程中调用删除/卸载方法),进一步加剧了状态不一致。
解决方案
1. 初始化PackageExtractionContext
在创建INuGetProjectContext实例时,显式初始化PackageExtractionContext,确保解压包所需的上下文参数齐全。
2. 修正PluginProject的安装逻辑
调整InstallPackageAsync方法,确保先调用基类完成包文件的安装,再通过PackagesConfigNuGetProject更新配置文件,同时修正原代码中的逻辑错误。
3. 明确Project职责边界
让FolderNuGetProject专注于包文件的下载与解压,PackagesConfigNuGetProject仅负责维护packages.config,避免职责混淆导致的异常。
代码修正示例
修正NugetPluginManager中的上下文初始化
public async Task Install(string repositoryUrl, string packageName, string version, bool includeDependencies, bool includePreRelease, CancellationToken cancellationToken = default) { // ... 其他代码保持不变 ... var projectContext = new EmptyNuGetProjectContext(); // 显式初始化PackageExtractionContext projectContext.PackageExtractionContext = new PackageExtractionContext( PackageSaveMode.Defaultv3, XmlDocFileSaveMode.None, ClientPolicyContext.GetClientPolicy(defaultSettings, logger), logger); await packageManager.InstallPackageAsync( packageManager.PackagesFolderNuGetProject, new PackageIdentity(packageName, NuGetVersion.Parse(version)), resolutionContext, projectContext, repository, Array.Empty<SourceRepository>(), cancellationToken); }
修正PluginProject的InstallPackageAsync方法
public class PluginProject : FolderNuGetProject { private readonly PackagesConfigNuGetProject packagesConfigProject; public PluginProject(string root) : base(root) { this.packagesConfigProject = new PackagesConfigNuGetProject(root, new Dictionary<string, object>() { { "TargetFramework", NuGetFramework.AnyFramework }, { "Name", "My.Package.Installer" } }); } public override Task<IEnumerable<PackageReference>> GetInstalledPackagesAsync(CancellationToken cancellationToken) { return this.packagesConfigProject.GetInstalledPackagesAsync(cancellationToken); } public override async Task<bool> InstallPackageAsync(PackageIdentity packageIdentity, DownloadResourceResult downloadResourceResult, INuGetProjectContext nuGetProjectContext, CancellationToken token) { // 先调用基类完成包文件的安装到packages目录 var result = await base.InstallPackageAsync(packageIdentity, downloadResourceResult, nuGetProjectContext, token); // 再更新packages.config await packagesConfigProject.InstallPackageAsync(packageIdentity, downloadResourceResult, nuGetProjectContext, token); return result; } }
验证结果
修正后,调用NuGetPackageManager.InstallPackageAsync时:
- 不再抛出空引用异常;
- 目标目录会生成
packages文件夹并包含安装的包文件; packages.config会正确记录已安装的包信息。
内容的提问来源于stack exchange,提问作者morleyc
相关产品推荐
相关产品推荐

