温馨提示:本文翻译自stackoverflow.com,查看原文请点击:xamarin.forms - Correct way to distribute NuGet package with floating version
nuget xamarin.forms nuget-package

xamarin.forms - 分发带有浮动版本的NuGet软件包的正确方法

发布于 2020-03-27 11:46:47

我正在创建一个依赖Xamarin.Forms的NuGet包。该软件包应该可以与任何最新版本的Forms一起正常工作,因此我将其设置为:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
    <PackageId>MyCompany.FormsExtras</PackageId>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Xamarin.Forms" Version="4.*" />
  </ItemGroup>
</Project>

建立并在本地发布以测试...

$ dotnet pack -c Release -p:Version=0.9.0
$ nuget add bin/Release/MyCompany.FormsExtras.0.9.0.nupkg -source ~/Dropbox/Packages/

在我运行这些命令时,Xamarin.Forms 4.1.0.555618是最新版本。

我现在正尝试将此软件包拉入现有项目,该项目直接依赖于不同的旧版本Xamarin.Forms:

  <ItemGroup>
    <PackageReference Include="Xamarin.Forms" Version="4.0.0.425677" />
  </ItemGroup>

...但是它无法添加此错误的软件包:

Detected package downgrade: Xamarin.Forms from 4.1.0.555618 to 4.0.0.425677. Reference the package directly from the project to select a different version. 
 MyCompany.ToDo.Forms -> MyCompany.FormsExtras 0.9.0 -> Xamarin.Forms (>= 4.1.0.555618) 
 MyCompany.ToDo.Forms -> Xamarin.Forms (>= 4.0.0.425677)

I was under the impression that the floating version specified in my package's PackageReference should have allowed this to work? Am I missing a step, or do I just misunderstand how floating versions work?

I've read through the MS article on package dependency resolution. I also tried searching on the error message and "floating version" but I'm only finding workarounds on the consumer side; I'd like to fix this on my packaging so the consumers don't have to jump through hoops.

Any help much appreciated…

查看更多

查看更多

提问者
J. Perkins
被浏览
211
zivkan 2020-03-21 20:32

TL;DR version: Change your PackageReference to use Version=4.0.0, or the same version used by the project with the lowest version, instead of Version=4.*.

There's a misunderstanding between a project and a package. A project can be used to create a package, but they have different features. In particular, floating versions are a features only of PackageReference, which is how a project defines its package dependencies. The docs say:

When using the PackageReference format, NuGet also supports using a wildcard notation, *, for Major, Minor, Patch, and pre-release suffix parts of the number. Wildcards are not supported with the packages.config format.

It's not explicit about the nuspec not supporting wildcards either (a package contains a nuspec, not PackageReferences), but it isn't supported hence why your package has a dependency of >= 4.1.0.555618. Then as Matt pointed out in the comments, you're getting the downgrade warning because of the nearest wins rule (and NuGet treats the downgrade as a warning, but the .NET Core SDK elevates it to an error. I have no idea if Xamarin does as well or not). If you want your package to support >= 4.0.0, then you need to change the MyCompany.FormsExtras project's PackageReference for Xamarin.Forms to version 4.0.0 (although you should use the exact version of the lowest version available, otherwise every project that uses your package will have a performance hit when it can't find an exact match of your package's dependency), not 4.*.

I joined the NuGet team a long time after wildcards were implemented, and I made no effort of trying to find a design spec, so I'm totally guessing, but I believe the reason why packing a project that uses 4.* does not result in the package supporting >= 4.0.0 is because NuGet is making a best effort guess about what package versions are supported to minimise runtime failures for developers using the package.

To understand, consider the most extreme case, using a wildcard of *. Unless NuGet is going to somehow test your project with every version of your dependency to check which versions of the package it's actually compatible with (totally infeasible to do so, and even if it were it would making packing so very slow), the easiest two options are to either use >= 0.0.0 as that's spiritually equivalent to *, or use the version of the dependency that was resolved the last time the project was restored.

使用>= 0.0.0是一个问题,因为如果软件包的第一个版本与当前版本相比可能有重大更改,或者您的项目可能正在使用最早版本中不提供的API。因此,尽管您的项目使用*,但它实际上并不与该依赖项的所有版本兼容,因此>= 0.0.0可能无法正常工作。您的项目使用的软件包的版本越旧或越多,该软件包的最旧版本与您的项目一起使用的可能性就越小。

同样,语义版本控制指定次要版本表示不间断的更改,但确实包含新的API。您的项目被打包到使用4.1.x依赖项的程序包中,NuGet无法知道1)该程序包是否严格符合语义版本控制(我的猜测是非常少)和2)如果您的项目使用的是API仅在4.1.x而不可用4.0.x鉴于并非所有软件包都严格遵守语义版本控制,因此即使更改4.1.*也不安全4.1.0

希望我已经说服您,NuGet的行为是将项目打包到程序包中时如何处理通配符的行为是最好的方法。它旨在最大限度地提高“开箱即用”的软件包的百分比。如果没有,您现在应该了解它是如何工作的,即使您不同意这是最好的实现。