gosha20777

Расставим точки над .NET Core и .NET Standard

February 22, 2018 | 17 Minute Read

В последнее время в компании Microsoft произошло много изменений. Поддержка Docker контейнеров в Windows Server 2016, нативное подключение через SSH в CMD (ура! можно забыть о PuTTY!), изменения в рендоре консоли в Windows, Unix решения для Azure, поддержка Pyton в Visual Studio, мощьный открытый редактор Visual Studio Code и Visual Studio для Mac OS. Microsoft действительно стала "ближе" к Unix, ближе к Open Source. Конечно не осталась в стороне и платформа .NET. Тут тоже произошло много приятных подвижек и нововведений. Если раньше C# + Linux был уделом энтузиастов, то теперь — это прекрасная альтернатива Java. Именно об этих технологиях и будут написано несколько моих следующих статей. Но сначала давайте разберемся с точками в .NET...

.NET Core и .NET Standard — самые новые на сегодняшний день технологии .NET, поэтому совершенно неудивительно, что не только они, но и их отличия от платформы .NET Framework вызывают много вопросов. В этой статье я расскажу, что эти технологии из себя представляют и в каких случаях будет более логично выбрать каждую их.

Среда .NET.

Перед тем как углубляться в детали, обсудим среду .NET в целом и место, которое в ней занимают .NET Core и .NET Standard.

Рис 1. (Структура .NET. Отличия.)

Платформа .NET Framework появилась 15 лет назад(!). Тогда в её состав входил только один стек технологий .NET, с помощью которого можно было разрабатывать приложения для ПК под управлением Windows и веб-приложения. Но шло время и с тех пор появились и другие реализации .NET, например, Xamarin для разработки мобильных приложений для iOS и Android и классических приложений для macOS. Потом пришел mono фреймворк, появилась идея о .NET vNext в 2014г. Стало понятно, что классический .NET Freamwork не обладает должной гибкостью. Так возникла идея о стандартизации базовых API.

Какое же место здесь занимают .NET Core и .NET Standard?

  • .NET Core — это самая новая реализация .NET. Это проект Open Source с версиями для нескольких ОС. .NET Core позволяет создавать кроссплатформенные консольные приложения, а также приложения и облачные службы ASP.NET Core.
  • .NET Standard — это набор базовых API (другое их название — BCL, библиотека базовых классов), которые должны поддерживаться во всех реализациях .NET. .NET Standard позволяет создавать библиотеки, подходящие для любых приложений .NET, вне зависимости от реализации .NET или операционной системы, в которой они выполняются.
Рис 2. (Среда .NET.)

.NET Core

.NET Core — это открытая универсальная платформа разработки, которая поддерживается корпорацией Майкрософт и сообществом .NET на сайте GitHub. Она является кроссплатформенной, поддерживает Windows, Mac OS и Linux и может использоваться на устройствах, в облаке, во внедренных системах и в сценариях IoT (Интернета вещей). В её основе лежат технологии .NET Framework и Silverlight. Она оптимизирована для мобильных и серверных рабочих нагрузок, поскольку обеспечивает поддержку самодостаточных развёртываний XCOPY.

Чтобы лучше понять, что такое .NET Core, давайте попробуем разработать небольшое приложение для .NET Core. При этом мы познакомимся с новыми программами командной строки. Разрабатывать решения .NET Core можно в Visual Studio 2017, но раз уж вы читаете эту статью, я предположу, что с Visual Studio вы знакомы неплохо, и буду рассказывать в первую очередь о новых возможностях.

При создании .NET одним из главных приоритетов была высокая скорость разработки приложений для Windows. На практике это означает, что разработка .NET была неразрывно связана с Visual Studio. Безусловно, Visual Studio — замечательный инструмент. Он позволяет работать эффективно, а его отладчик — однозначно лучший из тех, которые мне доводилось использовать.

Но в некоторых случаях Visual Studio — не самый удобный вариант. Допустим, вы хотите поэкспериментировать с .NET, чтобы изучить C#. Для этого не хотелось бы скачивать и устанавливать IDE объемом в несколько гигабайт… Ладно, существует Sharp Develop, скажите вы. Но, допустим, вы подключаетесь к компьютеру с Linux через SSH? Тогда работать через IDE вообще не получится. А может быть, вам просто нравится командная строка?

Для таких случаев создана отличная программа для работы в командной строке — .NET Core CLI. Основной компонент .NET Core CLI называется dotnet. Его можно использовать для решения практически любых задач разработки, в частности, для создания, сборки, тестирования и упаковки проектов. Давайте посмотрим, как это работает.

Пример 1

Для начала создадим и запустим консольное приложение Hello World (я буду использовать PowerShell для Windows, но в Bash для macOS или Linux все делается аналогично).

$ dotnet new console -o hello
$ cd hello
$ dotnet run
Hello World!

Команда dotnet new делает то же самое, что элемент меню File – New Project в Visual Studio. С её помощью можно создавать проекты различных типов. Используйте команду dotnet new, чтобы вывести список предустановленных шаблонов.

Давайте переместим часть логики в библиотеку классов. Для этого в дополнение к проекту hello создадим проект библиотеки классов.

$ cd ..
$ dotnet new library -o logic
$ cd logic

Здесь мы хотим описать логику, которая будет формировать сообщение Hello World. Поэтому изменим содержимое файла Class1.cs на следующее:

namespace logic
{
  public static class HelloWorld
  {
      public static string GetMessage(string name) => $"Hello {name}!";
  }
}

Переименуем файл Class1.cs в HelloWorld.cs.

$ mv Class1.cs HelloWorld.cs

Обратите внимание: обновлять файл проекта, чтобы отразить это изменение, не нужно. Новые файлы проекта в .NET Core уже включают все исходные файлы из каталога проекта. Поэтому при добавлении, удалении и переименовании файлов изменять проект больше не нужно. Это очень упрощает работу в командной строке.

Чтобы использовать класс HelloWorld, нужно добавить в приложение hello ссылку на библиотеку, в которой содержится логика. Для этого можно изменить файл проекта или воспользоваться командой dotnet add reference.

$ cd ../hello
$ dotnet add reference ../logic/logic.csproj

Теперь изменим файл Program.cs так, чтобы в нем использовался класс HelloWorld.

Обновление файла Program.cs для дальнейшего использования класса HelloWorld:

using System;
using logic;
namespace hello
{
	class Program
	{
		static void Main(string[] args)
		{
			Console.Write("What's your name: ");
			var name = Console.ReadLine();
			var message = HelloWorld.GetMessage(name);
			Console.WriteLine(message);
		}
	}
}

Чтобы собрать и запустить приложение, введите команду dotnet run.

$ dotnet run
What's your name: Immo
Hello Immo!

В командной строке также можно создавать тесты. Этот CLI поддерживает MSTest, а также популярную платформу xUnit. Давайте для примера воспользуемся xUnit.

$ cd ..
$ dotnet new xunit -o tests
$ cd tests
$ dotnet add reference ../logic/logic.csproj

Чтобы добавить тест, измените содержимое файла UnitTest1.cs, как показано ниже.

Добавление теста в файл UnitTest1.cs:

using System;
using Xunit;
using logic;
namespace tests
{
	public class UnitTest1
	{
		[Fact]
		public void Test1()
		{
			var expectedMessage = "Hello Immo!";
			var actualMessage = HelloWorld.GetMessage("Immo");
			Assert.Equal(expectedMessage, actualMessage);
		}
	}
}

Теперь можно запустить тесты с помощью команды dotnet test.

$ dotnet test
Total tests: 1. Passed: 1. Failed: 0. Skipped: 0.
Test Run Successful.

Пример 2

Рассмотрим более интересный пример: создадим простой веб-сайт ASP.NET Core.

$ cd ..
$ dotnet new web -o web
$ cd web
$ dotnet add reference ../logic/logic.csproj

Измените вызов app.Run в файле Startup.cs так, чтобы в нём использовался класс HelloWorld.

app.Run(async (context) =>
{
  var name = Environment.UserName;
  var message = logic.HelloWorld.GetMessage(name);
  await context.Response.WriteAsync(message);
});

Чтобы запустить тестовый веб-сервер, вновь введите команду dotnet run.

$ dotnet run
Hosting environment: Production
Now listening on: http://localhost:5000
Application started. Press Ctrl+C to shut down.

Откройте в браузере URL-адрес, который был выведен в консоли (это должен быть адрес localhost:5000).

Сейчас структура вашего проекта должна соответствовать вот такой структуре.

Структура созданного проекта:

$ tree /f
│
├───hello
│ hello.csproj
│ Program.cs
│
├───logic
│ HelloWorld.cs
│ logic.csproj
│
├───tests
│ tests.csproj
│ UnitTest1.cs
│
└───web
Program.cs
Startup.cs
web.csproj

Чтобы упростить редактирование файлов в Visual Studio, создадим файл решения *.SIN и добавим в него все проекты.

$ cd ..
$ dotnet new sln -n HelloWorld
$ ls -fi *.csproj -rec | % { dotnet sln add $_.FullName }

Как видите, .NET Core CLI — мощный и удобный инструмент, который покажется привычным даже тем разработчикам, которые никогда раньше не работали с .NET.

Ещё одно огромное преимущество .NET Core — поддержка самодостаточных развёртываний. Приложение можно поместить в контейнер Docker, содержащий собственную копию среды выполнения .NET Core. То есть благодаря контейнерам вы можете использовать различные версии .NET Core для запуска различных приложений на одном компьютере, и они не будут мешать друг другу. .NET Core — открытый продукт, поэтому вы можете пользоваться промежуточными сборками или даже версиями с модификациями, которые внесли вы сами. Но это уже совсем другая история.

.NET Standard

Современные приложения часто приходится адаптировать к устройствам различных типов, а значит и к различным реализациям .NET. Клиенты воспринимают как должное доступ к хранящимся в облаке данным через веб-приложение с мобильного телефона или веб-браузер с ноутбука. В собственной инфраструктуре вам, скорее всего, захочется пользоваться программами командной строки и позволить сотрудникам управлять системой с помощью классических приложений. В таблице ниже показано, для решения каких задач подходят различные реализации .NET.

Платформа ОС Open Source Назначение
.NET Framework Windows Нет Создание классических Windows-приложений и веб-приложений ASP.NET для IIS.
.NET Core Windows, Linux, macOS Да Создание кроссплатформенных консольных приложений, а также веб-приложений и облачных служб ASP.NET Core.
Xamarin iOS, Android, macOS Да Создание мобильных приложений для iOS и Android, классических приложений для macOS.
.NET Standard любая Да Создание библиотек, которые можно использовать в любых реализациях .NET, в том числе .NET Framework, .NET Core и Xamarin.

Создание библиотек, которые можно использовать в любых реализациях .NET, в том числе .NET Framework, .NET Core и Xamarin.

В такой среде поддержка общего кода становится сложной задачей. Нужно понять, какие API доступны, и убедиться, что общие компоненты работают только с теми API, которые поддерживаются во всех используемых реализациях .NET.

Для решения этой задачи и создан .NET Standard. По сути он представляет собой спецификацию. В каждой версии .NET Standard определен некоторый набор API. Все реализации .NET, соответствующие этой версии, должны поддерживать все эти API. Эту систему можно рассматривать как еще один стек .NET, для которого можно создавать только библиотеки (но не приложения). Именно эту реализацию .NET следует использовать при создании библиотек, которые будут применяться в различных системах.

Возможно, вы задаетесь вопросом, какие именно API входят в .NET Standard. Если вы знакомы с платформой .NET Framework, то¬, наверное, знаете про библиотеку BCL — мы ее уже упоминали.

BCL — это набор базовых API, не зависящих от инфраструктур пользовательского интерфейса и моделей приложений. В него входят простые типы, файловый ввод-вывод, сетевые API, API сериализации, XML и другое.

Каждый стек .NET реализует определенную версию .NET Standard. Обычно каждая новая версия реализации .NET реализует самую последнюю (на этот момент) версию .NET Standard.

Хорошая аналогия — язык HTML и браузеры. Представьте, что спецификация HTML — это .NET Standard, а различные браузеры — это реализации .NET (например, .NET Framework, .NET Core и Xamarin).

Пример 3

Вероятно, вы уже задаетесь вопросом, как можно пользоваться .NET Standard. На самом деле мы им уже воспользовались, когда создавали библиотеку с классом логики. Давайте взглянем на файл проекта внимательнее.

$ cd logic
$ cat logic.csproj
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netstandard2.0</TargetFramework>
  </PropertyGroup>

Сравним его с файлом проекта консольного приложения hello.

$ cd ..\hello
$ cat hello.csproj
<Project Sdk="Microsoft.NET.Sdk">

  <ItemGroup>
    <ProjectReference Include="..\logic\logic.csproj" />
  </ItemGroup>

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.0</TargetFramework>
  </PropertyGroup>

</Project>

Как видите, для параметра TargetFramework библиотеки логики задано значение netstandard2.0, а для соответствующего параметра консольного приложения — значение netcoreapp2.0. Параметр TargetFramework соответствует целевой версии реализации .NET. Таким образом, целевая реализация для консольного приложения — .NET Core 2.0, а для библиотеки — .NET Standard 2.0. Это значит, что к библиотеке логики можно обращаться не только из приложения .NET Core, но и из приложения для .NET Framework или Xamarin.

.NET Framework и .NET Standard. Совместимость.

К сожалению, для большей части современных библиотек целевой реализацией является не .NET Standard, а .NET Framework. Конечно, .NET Standard в качестве целевой версии подходит не для всех библиотек, и это совершенно нормально. Например, целевой реализацией для библиотеки, в которой содержатся элементы управления Windows Presentation Foundation (WPF), должна быть .NET Framework, потому что компоненты пользовательского интерфейса не являются частью стандарта. Однако с большей частью библиотек общего назначения ситуация другая: для них целевой реализацией является .NET Framework просто потому, что на момент их создания .NET Standard еще не существовало. И тут мы сталкиваемся с проблемой нехватки компонентов. Но Microsoft элегантно решила данную проблему.

В .NET Standard 2.0 уже содержится достаточно большой набор API для того, чтобы в подавляющей части библиотек общего назначения в качестве целевой реализации можно было использовать .NET Standard. В 70% библиотек, доступных сегодня в NuGet, используются только API, входящие в .NET Standard. Однако лишь немногие из них явным образом отмечены как совместимые с .NET Standard.

Чтобы разработчики могли использовать не только их, был добавлен режим совместимости. Если вы устанавливаете пакет NuGet, в котором нет библиотеки ни для вашей целевой платформы, ни для .NET Standard, то NuGet попробует использовать .NET Framework в качестве резервного варианта. Другими словами, вы можете обращаться к библиотекам .NET Framework так, как если бы для них в качестве целевой реализации был указан .NET Standard.

Так например, в процессе разработки проекта под Linux на C# мне пришлось общаться с базой данных MySQL. Для этого я использовал совместимую только с .NET Framework 4.6 библиотеку. Но проект успешно собрался под Linux и библиотека отработала на "ура".

Пример 4

Давайте посмотрим, как это работает. В моем примере мы использовали популярную библиотеку коллекций PowerCollections, которая была создана в 2007 году (верните мне мой 2007-ой). Она долго не обновлялась, и в качестве целевой платформы для нее по-прежнему указана .NET Framework 2.0(!). Добавим ее через NuGet в приложение hello.

$ dotnet add package Huitian.PowerCollections

Эта библиотека поддерживает дополнительные типы коллекций, которых нет в BCL. Один из них — тип Bag, не гарантирующий какого-либо порядка элементов. Изменим наше приложение hello так, чтобы в нем использовался этот тип.

Пример приложения с использованием PowerCollections:

using System;
using Wintellect.PowerCollections;
namespace hello
{
	class Program
	{
		static void Main(string[] args)
		{
			//Тот самый "баг" :-)
			var data = new Bag<int>() { 1, 2, 3 };
			foreach (var element in data)
				Console.WriteLine(element);
		}
	}
}

Если вы запустите программу, то увидите следующее:

$ dotnet run
hello.csproj : warning NU1701: Package 'Huitian.PowerCollections 1.0.0' was restored using '.NETFramework,Version=v4.6.1' instead of the project target framework '.NETCoreApp,Version=v2.0'. This may cause compatibility problems.
1
3
2

Компилятор выкинул warning но программа отработала!

Что же произошло? Целевой реализацией для приложения helloявляется платформа .NET Core 2.0. .NET Core 2.0 является одной из реализаций .NET Standard 2.0, поэтому она поддерживает режим совместимости для обращения к библиотекам .NET Framework. Однако некоторые библиотеки .NET Framework в определенных реализациях .NET работать не будут. Например, это библиотеки, в которых используются API Windows Forms или WPF. NuGet не может об этом знать, поэтому он выдает сообщение с предупреждением о возможных проблемах.

Конечно, неустранимые предупреждения, которые выводятся при каждой сборке, очень раздражают (а точнее БЕСЯТ!). Поэтому после проверки приложения вы можете отключить предупреждение, связанное с конкретным пакетом. Наше приложение работает правильно (корректно выводит содержимое созданной коллекции типа Bag), поэтому сейчас мы отключим это сообщение. Для этого изменим файл hello.csproj и добавим атрибут NoWarn в ссылку на пакет.

<PackageReference Include="Huitian.PowerCollections" Version="1.0.0" 
  NoWarn="NU1701" />

Если вы снова запустите приложение, предупреждения уже не будет. Однако, если вы установите другой пакет, в котором используется режим совместимости, появятся новые предупреждения. При необходимости их тоже можно будет отключить.

В новых проектах поддерживается возможность задать несколько целевых платформ и выполнить сборку одного проекта для нескольких реализаций .NET. Это значит, что вы можете адаптировать библиотеку к конкретным реализациям .NET с помощью механизма условной компиляции (#if). Также вы можете создавать оболочки .NET Standard для API, относящихся к конкретным платформам. Но это уже совсем другая история.

Еще новый набор инструментов позволяет создавать пакеты NuGet в процессе сборки проектов — библиотек классов. Таким образом можно легко предоставить доступ к библиотекам неограниченному числу пользователей (достаточно просто выложить их на nuget.org) или только сотрудникам вашей организации (для этого добавьте их в вашу ленту пакетов в Visual Studio Team Services или в MyGet).

Заключение

.NET Standard представляет собой спецификацию API, которые должны содержаться во всех реализациях .NET. Он делает семейство технологий .NET более организованным и позволяет разработчикам создавать библиотеки, которые можно использовать в любой реализации .NET. Этот стандарт заменяет библиотеки PCL в роли механизма создания общих компонентов.

.NET Core — реализация .NET Standard, оптимизированная для создания консольных приложений, веб-приложений и облачных служб с использованием ASP.NET Core. В состав соответствующего SDK входит несколько мощных инструментов, которые дополняют возможности Visual Studio, позволяя решать задачи разработки с помощью командной строки.

P.s.

...

На сегодняшний день C# стал полностью кроссплатформенным языком, что конечно не может не радовать! Я лишь буду ждать и наедятся на то, что Java побыстрее умрет))) Смерть жабе!

Дополнительная литература.