NuGet包下载目录异常:版本号与包ID混存问题求助
问题:NuGet包存储路径不符合预期
使用程序化代码下载NuGet包时,包已成功添加至MachineWideConfigDirectory,但实际存储路径为.nuget/packages/Microsoft.Extensions.Logging.6.0.0,而非预期的.nuget/packages/microsoft.extensions.logging/6.0.0。
原始代码与配置
程序化下载包的代码
var settings = Settings.LoadDefaultSettings(string.Empty, null, new MachineWideSettings()); var packageSourceProvider = new PackageSourceProvider(settings); var sourceRepositoryProvider = new SourceRepositoryProvider(packageSourceProvider, Providers); var framework = NuGetFramework.Parse(Assembly.GetEntryAssembly()!.GetCustomAttribute<TargetFrameworkAttribute>()!.FrameworkName!); var package = await this.GetPackageAsync(this.PackageId, this.PackageVersion, this.IncludePreRelease, false, cancellationToken).ConfigureAwait(false) ?? throw new ArgumentNullException($"Failed to find the specified package with id '{this.PackageId}' and version '{this.PackageVersion}'"); var project = new PluginNugetProject(this.PackagesDirectory.FullName, this.PluginDirectory.FullName, package.Identity, framework); var packageManager = new NuGetPackageManager(sourceRepositoryProvider, settings, this.PackagesDirectory.FullName) { PackagesFolderNuGetProject = project }; var clientPolicyContext = ClientPolicyContext.GetClientPolicy(settings, this.NugetLogger); var projectContext = new PluginNugetProjectContext(this.LoggerFactory) { PackageExtractionContext = new PackageExtractionContext(PackageSaveMode.Defaultv3, PackageExtractionBehavior.XmlDocFileSaveMode, clientPolicyContext, this.NugetLogger) }; var resolutionContext = new ResolutionContext(DependencyBehavior.Lowest, true, false, VersionConstraints.None); var downloadContext = new PackageDownloadContext(resolutionContext.SourceCacheContext, this.PackagesDirectory.FullName, resolutionContext.SourceCacheContext.DirectDownload); await packageManager.InstallPackageAsync(project, package.Identity, resolutionContext, projectContext, downloadContext, package.SourceRepository, new List<SourceRepository>(), cancellationToken).ConfigureAwait(false); await project.PostProcessAsync(projectContext, cancellationToken).ConfigureAwait(false); await project.PreProcessAsync(projectContext, cancellationToken).ConfigureAwait(false); await packageManager.RestorePackageAsync(package.Identity, projectContext, downloadContext, new[] { package.SourceRepository }, cancellationToken).ConfigureAwait(false);
MachineWideSettings实现
class MachineWideSettings : IMachineWideSettings { public ISettings Settings => NuGet.Configuration.Settings.LoadMachineWideSettings(NuGetEnvironment.GetFolderPath(NuGetFolderPath.MachineWideConfigDirectory)); }
nuget.config配置
<?xml version="1.0" encoding="utf-8"?> <configuration> <packageSources> <add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" /> <add key="Microsoft Visual Studio Offline Packages" value="C:\Program Files (x86)\Microsoft SDKs\NuGetPackages\" /> </packageSources> </configuration>
修正方案
核心原因
问题出在自定义的PluginNugetProject替换了NuGetPackageManager默认的PackagesFolderNuGetProject,导致NuGet v3标准的小写ID+版本子目录存储逻辑被覆盖。默认的PackagesFolderNuGetProject会自动处理包ID的小写转换和路径分层,而自定义项目未实现该逻辑。
具体修正步骤
移除自定义项目替换逻辑
不要将packageManager.PackagesFolderNuGetProject设置为自定义的PluginNugetProject,保留NuGet默认的实现。调整安装目标项目
调用InstallPackageAsync时,传入默认的packageManager.PackagesFolderNuGetProject作为安装目标,确保路径遵循v3规范。按需添加自定义目录处理
如果需要将包文件同步到插件目录,在包安装完成后,基于标准存储路径手动复制文件到目标目录。
修改后的关键代码
var settings = Settings.LoadDefaultSettings(string.Empty, null, new MachineWideSettings()); var packageSourceProvider = new PackageSourceProvider(settings); var sourceRepositoryProvider = new SourceRepositoryProvider(packageSourceProvider, Providers); var framework = NuGetFramework.Parse(Assembly.GetEntryAssembly()!.GetCustomAttribute<TargetFrameworkAttribute>()!.FrameworkName!); var package = await this.GetPackageAsync(this.PackageId, this.PackageVersion, this.IncludePreRelease, false, cancellationToken).ConfigureAwait(false) ?? throw new ArgumentNullException($"Failed to find the specified package with id '{this.PackageId}' and version '{this.PackageVersion}'"); // 保留默认的PackagesFolderNuGetProject,不替换为自定义项目 var packageManager = new NuGetPackageManager(sourceRepositoryProvider, settings, this.PackagesDirectory.FullName); var clientPolicyContext = ClientPolicyContext.GetClientPolicy(settings, this.NugetLogger); var projectContext = new PluginNugetProjectContext(this.LoggerFactory) { PackageExtractionContext = new PackageExtractionContext(PackageSaveMode.Defaultv3, PackageExtractionBehavior.XmlDocFileSaveMode, clientPolicyContext, this.NugetLogger) }; var resolutionContext = new ResolutionContext(DependencyBehavior.Lowest, true, false, VersionConstraints.None); var downloadContext = new PackageDownloadContext(resolutionContext.SourceCacheContext, this.PackagesDirectory.FullName, resolutionContext.SourceCacheContext.DirectDownload); // 使用默认项目执行安装,确保路径符合v3规范 await packageManager.InstallPackageAsync(packageManager.PackagesFolderNuGetProject, package.Identity, resolutionContext, projectContext, downloadContext, package.SourceRepository, new List<SourceRepository>(), cancellationToken).ConfigureAwait(false); // 自定义逻辑:将已安装的包内容复制到插件目录(示例) var standardPackagePath = Path.Combine( this.PackagesDirectory.FullName, package.Identity.Id.ToLowerInvariant(), package.Identity.Version.ToString() ); // 实现CopyDirectory方法完成目录复制 CopyDirectory(standardPackagePath, this.PluginDirectory.FullName); // 按需保留或调整自定义的Post/PreProcess逻辑 // await project.PostProcessAsync(projectContext, cancellationToken).ConfigureAwait(false); // await project.PreProcessAsync(projectContext, cancellationToken).ConfigureAwait(false); await packageManager.RestorePackageAsync(package.Identity, projectContext, downloadContext, new[] { package.SourceRepository }, cancellationToken).ConfigureAwait(false);
补充说明
- NuGet v3存储规范要求包ID必须小写,版本作为子目录存在于ID目录下,默认
PackagesFolderNuGetProject内部已封装该逻辑。 - 若
PluginNugetProject包含特定业务逻辑,建议仅用它处理后续的自定义流程,而非替换默认的包存储项目。
内容的提问来源于stack exchange,提问作者Charles d'Avernas
相关产品推荐
相关产品推荐

