Jak stworzyć pakiet NuGet wspierający wiele frameworków

Praca z .NET Standard to przyjemność. Ten framework jest ciekawy, zwięzły, niezwykle czytelny i prosty. Jako programista chciałbym pracować tylko z kodem .NET Core lub .NET Standard. Czasami jednak trzeba wspierać również stary .Net Framework. Jak stworzyć pakiet NuGet, który może być używany przez obie strony?

Przykład z życia wzięty

Jestem autorem i programistą prostego klienta dla API Egnyte, które obsługuje prywatną chmurę dla dokumentów z mnóstwem funkcji. Jest to pakiet, który stworzyłem jako PCL (Portable Class Library), ponieważ może obsługiwać wiele frameworków, jednocześnie programując w pełnym .Net Framework.

Jednak ostatnio poproszono mnie o napisanie obsługi .Net Standard, aby można go było używać w nowszych projektach. Z radością skorzystałem z okazji i zacząłem szukać informacji na ten temat.

Okazało się, że gdy trzeba obsługiwać wiele frameworków, preferowanym sposobem nie jest już PCL, ale właśnie .Net Standard. Wziąłem się zatem do pracy. Przeportowałem swój pakiet do .Net Standard i sprawiłem, że działa. Na szczęście jest to bardzo prosty projekt, więc nie było to trudne zadanie. Co więcej, jeśli chcesz obsługiwać wiele frameworków, wystarczy, że umieścisz je w swoim pliku .csproj.

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

  <PropertyGroup>
    <TargetFrameworks>netstandard2.0;net461</TargetFrameworks>
  </PropertyGroup>

<ItemGroup>
  <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
</ItemGroup>

</Project>

Zauważ, że zmieniłem TargetFramework na TargetFrameworks i teraz mogę wprowadzić listę frameworków, któą będą wspierał. Więcej na temat wspierania wielu frameworków na raz znajdziesz na tej stronie Microsoft.

Czemu się nie buduje?

Do tej pory wydaje się to niewiarygodnie łatwe, ale prawda jest taka, że może być jednak trochę trudniej. Kiedy zbudowałem swój projekt, dostałem wyjątek:

Error CS0234: The type or namespace name ‘Http’ does not exist in the namespace ‘System.Net’ (are you missing an assembly reference?)

Nie ma odwołania do biblioteki System.Net.Http, ponieważ typy które wymagają tej przestrzeni nazw, znajdują się wewnątrz .Net Standard. Nie musiałem jej dodatkowo importować. Jednak dla .Net Framework 4.6.1 potrzebuję dodać taki pakiet, aby mój kod działał. Po pewnym czasie doszedłem do wniosku, że potrzebuję pewnej modyfikacji w moim pliku .csproj.

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

  <PropertyGroup>
    <TargetFrameworks>netstandard2.0;net461</TargetFrameworks>
  </PropertyGroup>

   <!--Conditionally obtain references for the .NET Framework 4.6.1 target-->  
  <ItemGroup Condition=" '$(TargetFramework)' == 'net461' ">
    <Reference Include="System.Net.Http" />
  </ItemGroup>
  <ItemGroup>
  <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
</ItemGroup>

</Project>

Na szczęście był to jedyny problem, ale być może będziesz musiał zrobić więcej „if-ów”, a nawet dodać kilka haków w kodzie. Możesz np. napisać kod, który będzie działał tylko dla określonego frameworka.

#if NET40
        Console.WriteLine("Target framework: .NET Framework 4.0");
#elif NET45  
        Console.WriteLine("Target framework: .NET Framework 4.5");
#else
        Console.WriteLine("Target framework: .NET Standard 1.4");
#endif

Teraz moje drzewo projektu wygląda tak. Jak widać, teraz w Dependencies mam wymienione dwa frameworki.

Tworzenie pakietu NuGet

W .Net Standard nie ma prostszej rzeczy niż utworzenie pakietu NuGet. Wystarczy uruchomić polecenie dotnet pack w głównym folderze projektu.

W moim przypadku potrzebowałem więcej informacji o pakiecie, takich jak autor i opis, więc zmodyfikowałem mój plik .csproj i oto moja ostateczna wersja.

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

  <PropertyGroup>
    <TargetFrameworks>netstandard2.0;net461</TargetFrameworks>
    <PackageId>Egnyte.Api</PackageId>
    <Version>2.0.0-alpha1</Version>
    <Authors>Michal Bialecki</Authors>
    <Company>Egnyte Inc.</Company>
    <Description>Egnyte Api client for .net core</Description>
    <GenerateDocumentationFile>true</GenerateDocumentationFile>
  </PropertyGroup>

  <!--Conditionally obtain references for the .NET Framework 4.6.1 target--> 
  <ItemGroup Condition=" '$(TargetFramework)' == 'net461' ">
    <Reference Include="System.Net.Http" />
  </ItemGroup>
  <ItemGroup>
    <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
  </ItemGroup>

</Project>

Jedna z rzeczy, o której warto wspomnieć to <GenerateDocumentationFile>true</GenerateDocumntationFile>, ponieważ powoduje wygenerowanie dokumentacji XML wraz z bibliotekami dll. Właśnie stąd IntelliSense pobiera opisy metod, co jest bardzo przydatne dla programistów.

Teraz, jeśli otworzę mój pakiet NuGet w NuGet Package Explorer, widzę coś takiego:

Teraz mój pakiet obsługuje zarówno .Net Standard 2.0, jak i .Net Framework 4.6.1.

Zobaczmy, jak to wygląda podczas instalacji tego pakietu.

Pakiet Egnyte.Api zawiera informacje o tym, jaki framework obsługuje i że jest to wersja wstępna (Prerelease), ponieważ celowo ustawiłem wersję na 2.0.0-alpha1. Podczas instalowania pakietu NuGet Package Manager sam zdecyduje, która wersja biblioteki jest odpowiednia dla projektu w którym pracujesz i zainstaluje właściwą. Miło i łatwo, wszystko odbywa się automatycznie!

Summary

Wspieranie wielu frameworków w .NET Standard jest naprawdę proste, ale może być bardziej skomplikowane w przypadku korzystania z wielu odwołań i bibliotek. Jednak jest to warte wysiłku, ponieważ możesz pracować z .Net Standard zamiast ze starymi frameworkami i projektami.

Tworzenie i budowanie pakietu NuGet jest znacznie prostsze w .Net Standard i można korzystać ze wszystkich fajnych funkcji tej platformy. Będziesz także bardziej na bieżąco ze zmianami w najnowszych wersjach frameworka.

.Net Standard to nowy PCL, standard tworzenia bibliotek dla wielu frameworków i platform. Ponadto NuGet obsługuje wiele wersji biblioteki dll w jednym pakiecie i wybiera odpowiednią dla Ciebie. Jeśli odbywa się to automatycznie, dlaczego nie spróbować?

Cały kod, który znalazł się w tym poście jest dostępny w repozytorium na GitHub.

Dobrej zabawy!

 

Leave a Reply

Your email address will not be published. Required fields are marked *