Windows 10 2015 年特别版

第 30 卷,第 11 期

Visual Studio 工具 - NuGet 功能增强了 Windows 10 的开发功能

作者 Jeff Fitz | Windows 2015

现在 NuGet 团队提供了几款全新工具。它与 Microsoft 的一些团队合作推出了新版本的 NuGet 客户端,以支持通用 Windows 平台 (UWP) 和新的可移植类库 (PCL)。新的 NuGet 工具可通过“工具 | 扩展和更新 | Visual Studio 2015 中的更新”获取,还可以从 NuGet 分布站点获取,网址为:bit.ly/1MgNt2J。NuGet 还发布了新版本的 NuGet 命令行工具,您可以从 dist.nuget.org 上的相同位置下载它。本文将回顾这些新功能和 Windows 开发者向 Windows 10 项目添加 NuGet 支持需要遵循的进程。

Project.Json

从 ASP.NET 5 开始,NuGet 引入了 project.json 文件支持,以便使用您会立即依赖的程序包的明确定义来描述项目依赖项。在 ASP.NET 5 中,这是定义项目配置的唯一文件。但是借助 NuGet 3.1,您可以在通用 Windows 项目和现代 PCL(面向 DNX、UWP 和 Microsoft .NET Framework 4.6)中使用此文件来定义程序包引用。这种方法的好处就是 Visual Studio 中的“管理程序包”对话框将基于您所开发的项目类型,适当维护您的 packages.config 或 project.json 文件。

这一转变从 packages.config 模型开始,还允许您“重启”项目中的引用,并使用 NuGet 的新的可传递依赖项功能。开发者和程序包作者向 NuGet 团队报告,当他们将程序包添加到项目时,他们的 packages.config 文件会与其依赖程序包中的依赖项混淆。

例如,NHibernate 是依赖于 Iesi.Collections 程序包的程序包。在 packages.config 中有两个引用:NHibernate 和 Iesi.Collections。在更新 NHibernate 时,会遇到一个问题“我是否也要更新 Iesi.Collections?” 同样存在与此相反的问题。如果有适用于 Iesi.Collections 的更新,我是否需要更新 NHibernate 来支持 Iesi.Collections 中的新功能? 开发者可能会陷入对程序包引用所带来的项目程序包依赖项进行管理的令人厌恶的循环。

NuGet 的可传递依赖项功能简化了这一决定,通过程序包定义文件(nuspec 文档)中语义版本的改进支持更新程序包引用。开发者指定了其程序包支持的依赖项版本范围。当 NuGet 安装客户端时,这些依赖项会对 packages.config 文件中的指定版本添加硬引用,这些引用的程序包看上去就像您添加到项目中的任何其他程序包引用。您可以在图 1 中看到这一问题的极好示例。

图 1 ASP.NET MVC packages.config 文件的内容

<?xml version="1.0" encoding="utf-8"?>
<packages>
  <package id="Antlr" version="3.4.1.9004" targetFramework="net46" />
  <package id="bootstrap" version="3.0.0" targetFramework="net46" />
  <package id="EntityFramework" version="6.1.3" targetFramework="net46" />
  <package id="jQuery" version="1.10.2" targetFramework="net46" />
  <package id="jQuery.Validation" version="1.11.1" targetFramework="net46" />
  <package id="KendoUICore" version="2015.2.624" targetFramework="net46" />
  <package id="Microsoft.AspNet.Identity.Core" version="2.2.1" targetFramework="net46" />
  <package id="Microsoft.AspNet.Identity.EntityFramework"
    version="2.2.1" targetFramework="net46" />
  <package id="Microsoft.AspNet.Identity.Owin" version="2.2.1" targetFramework="net46" />
  <package id="Microsoft.AspNet.Mvc" version="5.2.3" targetFramework="net46" />
  <package id="Microsoft.AspNet.Razor" version="3.2.3" targetFramework="net46" />
  <package id="Microsoft.AspNet.Web.Optimization" version="1.1.3" targetFramework="net46" />
  <package id="Microsoft.AspNet.WebPages" version="3.2.3" targetFramework="net46" />
  <package id="Microsoft.CodeDom.Providers.DotNetCompilerPlatform"
    version="1.0.0" targetFramework="net46" />
  <package id="Microsoft.jQuery.Unobtrusive.Validation"
    version="3.2.3" targetFramework="net46" />
  <package id="Microsoft.Net.Compilers"
    version="1.0.0" targetFramework="net46" developmentDependency="true" />
  <package id="Microsoft.Owin" version="3.0.1" targetFramework="net46" />
  <package id="Microsoft.Owin.Host.SystemWeb" version="3.0.1" targetFramework="net46" />
  <package id="Microsoft.Owin.Security" version="3.0.1" targetFramework="net46" />
  <package id="Microsoft.Owin.Security.Cookies" version="3.0.1" targetFramework="net46" />
  <package id="Microsoft.Owin.Security.Facebook" version="3.0.1" targetFramework="net46" />
  <package id="Microsoft.Owin.Security.Google" version="3.0.1" targetFramework="net46" />
  <package id="Microsoft.Owin.Security.MicrosoftAccount"
    version="3.0.1" targetFramework="net46" />
  <package id="Microsoft.Owin.Security.OAuth" version="3.0.1" targetFramework="net46" />
  <package id="Microsoft.Owin.Security.Twitter" version="3.0.1" targetFramework="net46" />
  <package id="Microsoft.Web.Infrastructure" version="1.0.0.0" targetFramework="net46" />
  <package id="Modernizr" version="2.6.2" targetFramework="net46" />
  <package id="Newtonsoft.Json" version="6.0.4" targetFramework="net46" />
  <package id="Owin" version="1.0" targetFramework="net46" />
  <package id="Respond" version="1.2.0" targetFramework="net46" />
  <package id="WebGrease" version="1.5.2" targetFramework="net46" />
</packages>

在我将这些内容添加到项目中时,我实际上只需要 Microsoft.AspNet.Mvc、Microsoft.AspNet.Identity.EntityFramework、Newtonsoft.Json 和 Microsoft.Owin.Security.MicrosoftAccount。这四个程序包所引用的其他项目都只是干扰,现在我有特定版本的硬引用。通过可传递依赖项功能,不再需要其他这些程序包的版本。我只需管理我真正要在项目中使用的四个库。

NuGet 客户端会在后台为您解决和管理其他这些程序包,并将这些引用保持在项目中正在使用的程序包所声明的依赖版本约束范围内。这会极大地简化项目引用体验。

常见的本地程序包缓存

开发者通常有自己倾向使用的一些程序包和工具。在您明显已经在某个项目中拥有这些程序包和工具并想在另一个项目中使用时,为什么需要在单个工作站上多次下载并安装它们? 通过 project.json 托管的项目,NuGet 下载这些程序包的副本并将其存储在位于 %userprofile%\.nuget\packages 文件夹的全局程序包文件夹中。这会减少在工作站上使用磁盘空间。还能避免额外调用,这些调用从 NuGet.org 提取程序包来获取您已经拥有的项目。

Project.json 和常见的本地程序包缓存支持可供包含 NuGet 3.0 的 ASP.NET 5 以及其他始于 NuGet 3.1 的项目类型使用。

弃用的功能

从 NuGet 3.1 开始,在使用 project.json 时,弃用对执行 install.ps1/­uninstall.ps1 脚本和在 /content 程序包文件夹中传递元素的支持。使用这些元素安装程序包既不会执行 install.ps1 文件,也不会将内容复制到您的项目中。但是,在仍使用 packages.config 文件的项目中,仍然支持执行当前行为。出现这一情况有几个原因:

  • 随着可传递程序包还原,无法可靠地选取要卸载和安装的程序包。
  • 在将内容复制到用户项目,且程序包进行了更新时,会存在您无法可靠运行的隐式卸载过程。
  • NuGet 需要完全支持 Visual Studio 之外的开发。随着转向支持完全跨平台 .NET 开发体验,Windows Powershell 在其他环境中不可用。更多的开发者还致力于开发 Visual Studio 之外的 .NET 代码,他们需要支持。
  • 其他程序包管理器提供了管理和传递内容的卓越体验。NuGet 可以作为 .NET Framework 的程序包管理器良好地工作,因此,鼓励继续使用。
  • 不再支持“任何”框架。您无法再将文件直接放置于版本的根和库文件夹中,并将它们传送给项目。声明您的文件所支持的框架是非常重要的,这样,NuGet 才能了解解析这些引用的优先级顺序。
  • 不再支持解决方案包。这些程序包不能修改任何指定项目的功能,通常用于提供跨项目重复使用的共享资源。借助新共享的程序包文件夹,这些资源可能已经位于另一个项目的磁盘上。

新目标框架

NuGet 新版本的另一方面是支持新开发框架以及跨操作系统和体系结构的改进的本机程序包支持。NuGet 进一步探索了托管 .NET Framework 模型之外的环境,以支持更多的生态系统,使您可以将库部署到以前无法访问的环境。

目标框架名字对象 (TFM) 是一种简写形式,用于创建程序包来声明二进制支持的框架以及每个框架需要的依赖项。您将在使用此表示法的程序包库和 ref 文件夹中找到文件夹的名称。程序包的 nuspec 依赖项元素中还有一些元素,这些元素使用一个 TFM 值声明目标框架属性来指示 NuGet 客户端将相应的库传递给使用项目。

下列 TFM 仍可用,引入的新 TFM 如图 2 所示。

图 2 NuGet 3.x 支持的目标框架

说明 基础代码 可用版本
托管框架应用程序(Windows 窗体、控制台应用程序、Windows Presentation Foundation 和 ASP.NET) net net11、net20、net35、net35-client、net35-full、net4、net40、net40-client、net40-full、net403、net45、net451、net452 和 net46
ASP.NET 5 dnxcore dnxcore50
Windows 应用商店 netcore win8 = netcore45、win81 = netcore451 和 uap10.0
Windows Phone(appx 模型) wpa wpa81
Windows Phone (Silverlight) wp wp7 = sl3-wp、wp71 = sl4-wp71、sl4-wp、wp8 = wp8- 和 wp81
Silverlight sl sl2、sl3 = sl30、sl4 = sl40 和 sl5 = sl50
Xamarin   mono、MonoMac、Xamarin.Mac、MonoAndroid10、MonoTouch10 和 Xamarin.iOS10
Compact Framework net-cf net20-cf、net35-cf = cf35 和 net40-cf
Micro Framework netmf netmf41、netmf42 和 netmf43

所列出的带有等于 (=) 符号的项是 NuGet 支持的同义词。针对许多不同的框架有许多支持,但这可能会造成混淆。您是否需要为托管框架包中的微框架提供支持? 您需要获得多少 Silverlight 支持? 您需要回答这些问题,以便最符合使用者的需求。

您会发现表中没有支持 PCL 的显式调用。尽管 NuGet 支持这些框架的组合,但它希望您拥有适用于现代 PCL 的更加向前兼容的名字对象。这会在您构造程序包和定义支持框架时提供更多的灵活性。NuGet 3.1 引入了适用于现代 PCL 的 dotnet 目标名字对象。

Dotnet 目标名字对象

在 NuGet 的早期版本中,您可以指定带有 PCL 的框架,该框架作为后接加号符号的 TFM 缩写的集合。您可能会使用类似“portable-net45+win8+wpa81+wp8”作为文件夹名称的结尾。 这可能会造成混淆,给使用者带来不兼容的问题。为了使 PCL 和跨平台开发体验更加简单,NuGet 引入了 dotnet 名字对象。

此名字对象不会直接与任何指定版本或框架功能绑定。这是一个间接的引用,告诉 NuGet“如果它支持您拥有的框架和运行时功能的话,这是您应该使用的应用。” 然后 NuGet 客户端研究该引用以确定其支持的功能和框架。这一进程会持续,直到 NuGet 客户端解析 dotnet 引用支持的确切功能。然后,只有在引用匹配项目的功能和需求的情况下,客户端才会应用该引用。您可以通过 .NET Framework 4.5 以及后续派生的 Framework 版本(包括 Xamarin Android 和 Xamarin iOS)来引用 dotnet 名字对象。

这并不意味着您只需构建 PCL,并将其与声明的 dotnet 依赖项捆绑在一起,就完成操作了。如果您想要使用旧版本的 Visual Studio 和通过传统可移植类库构建的 NuGet 客户端来支持项目,则应创建一个引用,并将其放置于完整 PCL 目标框架名字对象中。

在将程序包安装到与 dotnet 名字对象(.NET Framework 4.6、UWP 或 ASP.NET 5)完全兼容的项目类型时,最后会搜寻 dotnet 名字对象。在尝试查找与项目的框架或不太具体的框架相匹配的引用后,将会引发上述操作。该层次结构如图 3 所示。

框架层次结构,检查其中是否存在对通用 Windows 平台项目的引用
图 3 框架层次结构,检查其中是否存在对通用 Windows 平台项目的引用

如果您的项目是使用仅针对这些框架中的任一个框架的 project.json 的现代 PCL,则会首先分析 dotnet 名字对象。后跟一个标准的 PCL 解决策略,如图 4 所示。

框架层次结构,检查其中是否存在对现代可移植类库项目的引用
图 4 框架层次结构,检查其中是否存在对现代可移植类库项目的引用

NuGet 命令行

现在 NuGet 类似于命令的可执行文件 nuget.exe 可用于支持将程序包安装、更新和还原到包含 packages.config 文件或 project.json 文件的项目。pack 命令可以继续使用磁盘上的 nuspec 文件和 packages.config 文件。它并没有更新为基于 project.json 文件生成 nuspec 文件。要解决此问题,您需要针对任何通过 project.json 程序包引用构造的新程序包内容构建您自己的 nuspec 文件。未来版本中将包含解决此问题的更新。

此版本的命令行可执行文件还支持 NuGet.org v3 终结点。此 nuget.org 源的新版本提供更快的交互,同时也是更加可靠的服务。通过内置的冗余和内容传送网络可以帮助快速传递程序包。从以下网址下载更新 NuGet.exe 的副本:bit.ly/1UV0kcU

如果您在升级 NuGet 扩展后安装了 Windows 10 SDK/Windows 10 工具,则安装程序会将此扩展降级为版本 3.1。您需要重新将其至少更新为 3.1.1 版本。在此“扩展和更新”对话框中显示的版本是 3.1.60724.766。Windows PowerShell 控制台是 3.1.1.0。

总结

现已提供支持 Windows 10 UWP 应用程序开发和 PCL 项目的功能。这些更改是广泛使用程序包管理器和 .NET Framework 的第一步。Microsoft 持续改进 .NET 开发体验,并专注于提供程序包管理器,以支持所有 .NET 开发者在任何平台上构建任何类型的项目。


Jeffrey T. Fritz是就职于 Microsoft NuGet 团队的高级项目经理。他喜欢长时间地在海滩上散步,并处理在云中缩放的 Web 应用程序。您可以通过 jefritz@microsoft.com 与他联系。

衷心感谢以下技术专家对本文的审阅: Microsoft NuGet 团队成员