Tag Archives: .net standard

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!

 

How to create a NuGet package targeting multiple frameworks

Working with .NET Standard is a pleasure. It’s fun, it’s short, it’s amazingly readable and concise. As a developer, I would like to work only with .NET Core or .NET Standard code. However, sometimes you need to support the old .Net Framework as well. How to create a NuGet package, that can be used by both?

A real-life example

I’m an author and a developer of a simple client for an Egnyte API, which is a private cloud for documents with a ton of features. It is a package, that I created as PCL (Portable Class Library), cause it can support multiple frameworks while still programming in full .Net Framework. 

However recently I was asked to write support for a .Net Standard as well so that it can be used by newer projects. I happily accepted the opportunity and started research around the topic. 

It turned out, that when you need to support multiple frameworks, the preferred way is no longer PCL, but .Net Standard. So that’s what I did. I ported my package to .Net Standard and make it work. Luckily it is a very simple project, so that wasn’t a very hard task. What’s more, if you would like to support multiple frameworks, you just need to list them in your .csproj file.

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

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

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

</Project>

Notice that I changed TargetFramework to TargetFrameworks and now I can provide a list of frameworks to support. More of targeting multiple frameworks you can read on this Microsoft page.

Why it doesn’t build?

It seems unbelievable easy up to this point, but the truth is that it can get more tricky. When I built my project, I got an exception:

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

System.Net.Http library could not be found, because types that are in this namespace are a part .Net Standard. I didn’t need to import it. However, for .Net Framework 4.6.1 I need to import this package to make my code work. So after some digging, I figured out, I need to modify my .csproj file.

<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>

Luckily that was the only problem, but you might need to do more if-s and even hacking inside the code with code that would run only for a specific framework.

#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

Now my project tree looks like this. As you can see, now in the Dependencies I have two frameworks listed.

Creating a NuGet package

With .Net Standard there is no easier thing to do, then creating a NuGet package. You just need to run a command dotnet pack in your main project folder.

In this case, I needed some more information for a package, like an author and description, so I modified my .csproj file and here is my final version.

<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>

One thing worth noticing is <GenerateDocumentationFile>true</GenerateDocumntationFile>, cause it generates XML documentation along with dlls. This is where IntelliSense takes it’s method descriptions from, which is very useful for developers.

Now if I open my NuGet package in the NuGet Package Explorer, I see something like this:

Now it supports both .Net Standard 2.0 and .Net Framework 4.6.1.

Let’s see how it looks like when installing this package.

Egnyte.Api package includes information about what framework it supports and that it is a prerelease, cause I intentionally set the version to 2.0.0-alpha1. While installing the package NuGet Package Manager will decide which dll is right for you project framework and install the right one. Nice and easy, everything is done automatically!

Summary

Targetting multiple frameworks in .NET Standard is really simple, but it can get harder when using many references and libraries. However, it is worth the pain, cause you could work with .Net Standard instead of old frameworks and projects.

Creating and building a NuGet package is way simpler and you can use all the .Net Standard cool features. You also would be more up-to-date with .Net framework changes.

.Net Standard is the new PCL, a standard for building libraries for multiple frameworks and platforms. Also, NuGet supports having multiple dlls in one package and it chooses the right one for you. If it is done automatically, why not try it out?

All code mentioned here is available in egnyte-dotnet GitHub repository.

Enjoy!