Special Windows 10 issue 2015

Volume 30 Number 11

Инструментарий Visual Studio - Новые средства NuGet для разработки под Windows 10

Джеффри Т. Фриц | Windows 2015

Продукты и технологии:

Windows 10, Visual Studio 2015, ASP.NET 5, NuGet 3.1

В статье рассматриваются:

  • добавление поддержки NuGet в проекты для Windows 10;
  • определение пакетов и зависимостей;
  • обеспечение совместимости между пакетами.

Группа NuGet выпустила несколько новых утилит. Мы работали в сотрудничестве с несколькими другими группами в Microsoft, чтобы предоставить вам новую версию клиента NuGet, который поддерживает Universal Windows Platform (UWP) и новые Portable Class Libraries (PCL). Новые утилиты NuGet доступны через Tools | Extensions and Updates | Update in Visual Studio 2015, а также на сайте дистрибуции NuGet (bit.ly/1MgNt2J). NuGet также выпустила новую версию утилиты командной строки NuGet, которую можно скачать с dist.nuget.org. В этой статье будет дан обзор новых возможностей и процесса, которому должны следовать Windows-разработчики, чтобы добавить поддержку NuGet в свои проекты для Windows 10.

Project.Json

Начиная с ASP.NET 5, группа NuGet ввела поддержку файла project.json, который описывает зависимости проекта с четким определением пакетов, от которых вы зависите напрямую. В ASP.NET 5 это единственный файл, определяющий конфигурацию проекта. Однако с появлением NuGet 3.1 вы используете этот файл в проектах для UWP и современных PCL (ориентированных на DNX, UWP и Microsoft .NET Framework 4.6) для определения ссылок вашего пакета. Хорошая новость в том, что диалог Manage Packages в Visual Studio будет соответственно поддерживать ваш файл packages.config или project.json за вас, исходя из типа разрабатываемого проекта.

Этот отход от модели packages.config также позволяет вам «перезапускать» ссылки в своих проектах и использовать новые возможности транзитивных зависимостей (transitive dependency capabilities) в 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. Содержимое файла packages.config в ASP.NET MVC

<?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 и общего локального кеша пакетов доступна для ASP.NET 5 с NuGet 3.0, а для других типов проектов нужен NuGet версии не ниже 3.1.

Устаревшие средства

Начиная с NuGet 3.1 при условии использования project.json, вы прекращаете поддерживать выполнение скриптов install.ps1/uninstall.ps1 и создание элементов в папке пакетов /content. Установка пакетов с этими элементами не позволит ни выполнить файл install.ps1, ни скопировать контент в ваш проект. Однако в проектах, которые все еще используют файлы packages.config, текущее поведение пока поддерживается. На то есть несколько причин:

  • при восстановлении транзитивных пакетов надежный выбор того, что нужно удалить, а что — установить, невозможен;
  • при копировании контента в проект пользователя, если пакеты обновляются, запускается неявный процесс удаления, который нельзя безопасно выполнять;
  • NuGet должен полностью поддерживать разработку вне Visual Studio. С переходом на полную поддержку кросс-платформенной .NET-разработки следует учитывать, что в других средах Windows Powershell недоступен. Кроме того, все больше разработчиков работают над .NET-кодом вне Visual Studio, и их нужно поддерживать;
  • другие диспетчеры пакетов обеспечивают отличное управление и доставку контента. NuGet хорошо работает как диспетчер пакетов для .NET Framework, поэтому для данной инфраструктуры рекомендуется по-прежнему использовать именно его;
  • больше нет поддержки «любой» инфраструктуры. Вы больше не можете помещать файлы напрямую в корень папок build и lib и включать их в проект. Важно объявить, какие инфраструктуры поддерживают ваши файлы, чтобы NuGet знал приоритет в разрешении соответствующих ссылок;
  • пакеты решения больше не поддерживаются. Эти пакеты не модифицируют какие-либо специфические возможности проекта и обычно служили для доставки общих ресурсов, которые повторно использовались между проектами. С появлением новой папки общих пакетов эти ресурсы уже могут находиться на диске, если они есть в каком-то другом проекте.

Новые целевые инфраструктуры

Другой аспект новой версии NuGet — поддержка новых инфраструктур разработки и улучшенная поддержка «родных» для разных операционных систем и архитектур пакетов. NuGet выходит за рамки одной лишь модели .NET Framework для поддержки большего количества экосистем и позволяет задействовать библиотеки в ранее недоступных средах.

Моникеры целевых инфраструктур (target framework monikers, TFM) — это сокращение (shorthand), применяемое при создании пакетов для объявления поддержки двоичных файлов конкретных инфраструктур и их зависимостей. Вы найдете в папках lib и ref пакета имена папок, где используется эта нотация. Кроме того, есть элементы в элементе зависимостей nuspec пакета, которые объявляют атрибут целевой инфраструктуры с помощью одного из TFM-значений, чтобы указать клиенту NuGet доставить соответствующую библиотеку в проект, где она должна использоваться.

В табл. 1 перечислены по-прежнему доступные TFM и новые TFM.

Табл. 1. Целевые инфраструктуры, поддерживаемые NuGet 3.x

Описание Базовый код Доступные версии
Приложения управляемых инфраструктур (Windows Forms, Console Applications, 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 Store 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 требуется именно вам? Вы должны ответить на эти вопросы, чтобы лучше удовлетворить потребности своих пользователей.

Вы заметите, что в табл. 1 нет явного названия для поддержки PCL. Хотя NuGet поддерживает комбинации инфраструктур, мы хотим, чтобы у вас был более совместимый «снизу вверх» (forward-compatible) моникер для современных PCL. Это даст вам большую гибкость в конструировании пакетов и определении поддерживаемых вами инфраструктур. В NuGet 3.1 введен целевой моникер dotnet (dotnet target moniker) для современных PCL.

Целевой моникер dotnet

В предыдущих версиях NuGet вы могли указывать инфраструктуры с какой-либо PCL, которая работала как набор TFM-аббревиатур, соединенных знаками «плюс». В итоге вы могли получить такие имена папок, как «portable-net45+win8+wpa81+wp8». Это могло создать путаницу и привести к проблемам несовместимости для ваших пользователей. Чтобы упростить кросс-платформенную разработку и создание PCL, NuGet ввела моникер dotnet.

Этот моникер не привязан напрямую к возможностям какой-либо конкретной версии или инфраструктуры. Это непрямую ссылка, которая сообщает NuGet: «Это ссылка, которую следует использовать, если она поддерживает имеющиеся инфраструктуру и возможности исполняющей среды». Тогда клиент NuGet анализирует эту ссылку, чтобы определить, какой функционал и инфраструктуры она поддерживает. Этот процесс продолжается, пока клиент NuGet не разрешит точные возможности, поддерживаемые ссылкой dotnet. Затем он применит ее, только если она соответствует средствам и требованиям вашего проекта. Вы можете ссылаться на моникер dotnet в .NET Framework версий от 4.5 и выше, а также в соответствующих версиях производных инфраструктур, включая Xamarin Android и Xamarin iOS.

Это не означает, что вы можете просто создать PCL, упаковать с объявленными зависимостями dotnet, и на этом все. Если вы хотите иметь возможность поддерживать проекты, использующие более старые версии Visual Studio и клиентов NuGet, созданных с помощью традиционных библиотек портируемых классов, то все равно должны создать и поместить ссылку на полный моникер целевой инфраструктуры PCL.

При установке пакета в проект того типа, который полностью совместим с моникером dotnet (.NET Framework 4.6, UWP или ASP.NET 5), моникер dotnet будет последним в процессе поиска. Это произойдет после попытки найти ссылку, которая совпадает с конкретной или менее специфичной инфраструктурой вашего проекта. Такая иерархия выглядит, как показано на рис. 2.

Иерархия инфраструктур, проверяемых на ссылки для проекта Universal Windows Platform
Рис. 2. Иерархия инфраструктур, проверяемых на ссылки для проекта Universal Windows Platform

Если ваш проект — это современная PCL, использующая project.json, который ориентирован на какую-либо из этих инфраструктур и ни на какую другую, моникер dotnet будет анализироваться первым. Процесс будет следовать стандартной стратегии разрешения PCL, как на рис. 3.

Hierarchy of Frameworks Inspected for References in a Modern Portable Class Library Project
Рис. 3. Иерархия инфраструктур, проверяемых на ссылки в проекте Modern Portable Class Library

dotnet dotnet
PCL containing your targets PCL, содержащая ваши целевые инфраструктуры
portable-* portable-*

Командная строка NuGet

Теперь доступен подобный команде исполняемый файл для NuGet, nuget.exe, и он поддерживает установку, обновление и восстановление пакетов в проекте либо через файл packages.config, либо через файл project.json. Команда pack по-прежнему работает с файлами nuspec на диске и файлами packages.config. Она не обновлялась для генерации файла nuspec на основе project.json. Чтобы обойти эту проблему, вам придется создавать собственный файл nuspec для любого нового контента пакета, конструируемого с использованием ссылки на пакеты, которые используют project.json. В следующий выпуск будет включено обновление, устраняющее эту проблему.

Данная версия исполняемого файла командной строки также поддерживает конечные точки NuGet.org v3. Эта новая версия канала nuget.org ускоряет взаимодействие и является более надежным сервисом. В быстрой доставке пакетов помогает встроенная поддержка избыточности и включенная сеть доставки контента (content-delivery network, CDN). Скачайте копию обновленного NuGet.exe по ссылке bit.ly/1UV0kcU.

Если вы установили Windows 10 SDK или Windows 10 Tools после обновления расширения NuGet, установщик откатит расширение обратно к версии 3.1. Из-за этого вам потребуется снова обновить его минимум до версии 3.1.1. Версия, показываемая в диалоге Extensions and Updates, — 3.1.60724.766. Консоль Windows PowerShell имеет версию 3.1.1.0.

Заключение

Теперь вам доступны средства, поддерживающие разработку приложений и проектов PCL для Windows 10 UWP. Эти изменения являются первым шагом в более широком применении диспетчера пакетов и .NET Framework. Microsoft продолжает улучшать .NET-разработку и в ближайшее время будет сосредоточена на том, чтобы предоставить диспетчер пакетов, пригодный для всех .NET-разработчиков проектов любых типов на любой платформе.


Джеффри Т. Фриц (Jeffrey T. Fritz) — старший менеджер программ в Microsoft, работает в группе NuGet. Любит долгие прогулки по пляжу и «убойные» веб-приложения, масштабируемые в облаке. С ним можно связаться по адресу jefritz@microsoft.com.

Выражаю благодарность за рецензирование статьи членам группы NuGet в Microsoft.