Monthly Archives: August 2020

Wykonanie procedury składowanej w Entity Framework Core

Procedury składowane są integralną częścią każdej bazy danych MS SQL. Są idealne do opakowania skomplikowanego kodu SQL w obiekt bazy danych, którego możemy ponownie użyć. Jak wykonać procedurę składowaną w Entity Framework Core 5? Przekonajmy się.

Dodanie procedury składowanej

Przede wszystkim musimy dodać procedurę składowaną. Najlepszym sposobem na to jest dodanie migracji bazy danych z odpowiednim kodem SQL. Zacznijmy od dodania migracji za pomocą polecenia globalnego narzędzia EF Core:

dotnet ef migrations add spUpdateProfilesCountry

To polecenie wygeneruje migrację, w której możemy umieścić nasz SQL. Zobaczmy, jak to może wyglądać:

public partial class spUpdateProfilesCountry : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        var sql = @"
            IF OBJECT_ID('UpdateProfilesCountry', 'P') IS NOT NULL
            DROP PROC UpdateProfilesCountry
            GO

            CREATE PROCEDURE [dbo].[UpdateProfilesCountry]
                @StardId int
            AS
            BEGIN
                SET NOCOUNT ON;
                UPDATE Profiles SET Country = 'Poland' WHERE LEFT(TelNo, 2) = '48' AND Id > @StardId
            END";

        migrationBuilder.Sql(sql);
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.Sql(@"DROP PROC UpdateProfilesCountry");
    }
}

Jest to prosty kod SQL, który najpierw sprawdza, czy procedura istnieje, a jeśli tak, usuwa ją. Następnie tworzy nową procedurę z nazwą UpdateProfilesCountry, która zaktualizuje kolumnę Country dla każdego profilu, którego numer telefonu zaczyna się od 48.

Kiedy migracja zostanie wykonana na bazie danych, stworzy procedurę składowaną UpdateProfilesCountry.

Wykonanie procedury składowanej

Nie ma dedykowanej metody wykonywania procedury składowanej, więc w przypadku, gdy procedura składowana nie zwraca danych, możemy po prostu wywołać ją jako zwykły kod SQL. Można to osiągnąć w następujący sposób:

await primeDbContext.Database.ExecuteSqlInterpolatedAsync(
    "UpdateProfilesCountry @p0",
    parameters: new[] { minimalProfileId.ToString() });

Kiedy odpytam bazę danych o wszystkie numery zaczynające się od 48, to zobaczę, że kraj został zaktualizowany na Poland. Oznacza to, że nasza procedura została poprawnie wykonana.

BTW. Nie przejmuj się że widzisz prawdziwe dane osobowe. Są to dane fałszywe, wygenerowane przy pomocy biblioteki Bogus 🙂

Podsumowanie

Entity Framework Core całkiem dobrze radzi sobie z procedurami składowanymi. Nie ma dedykowanej metody uruchamiania procedury, ale można ją uruchomić jako standardowy kod SQL. Jeśli jednak chcesz pobrać dane z bazy danych przy pomocy procedury składowanej, to jest to już inne podejście. Opisałem je dokładnie tutaj: Pobranie danych za pomocę procedury składowanej w Entity Framework Core 5.

Co więcej, możesz obsługiwać dodawanie lub aktualizowanie procedur składowanych za pomocą migracji EF Core, co oznacza, że wszystkie wymagane prace można wykonać za pomocą EF Core.

Cały wymieniony tutaj kod można znaleźć na moim GitHubie, możesz z nim eksperymentować do woli.

Pozdrowienia!

Execute a stored procedure with Entity Framework Core 5

Stored procedures are an integral part of any MS SQL database. They are perfect to wrap complicated SQL into a database object, that we can reuse. How to execute a stored procedure in Entity Framework Core 5? Let’s have a look.

Adding a stored procedure

First of all we need to add a stored procedure. The best way to do so is to add a database migration with an appropriate SQL. Let’s start by adding a migration with EF Core global tool command:

dotnet ef migrations add spUpdateProfilesCountry

This will generate a migration, that we can put our SQL into. Let’s see how it may look:

public partial class spUpdateProfilesCountry : Migration
{
    protected override void Up(MigrationBuilder migrationBuilder)
    {
        var sql = @"
            IF OBJECT_ID('UpdateProfilesCountry', 'P') IS NOT NULL
            DROP PROC UpdateProfilesCountry
            GO

            CREATE PROCEDURE [dbo].[UpdateProfilesCountry]
                @StardId int
            AS
            BEGIN
                SET NOCOUNT ON;
                UPDATE Profiles SET Country = 'Poland' WHERE LEFT(TelNo, 2) = '48' AND Id > @StardId
            END";

        migrationBuilder.Sql(sql);
    }

    protected override void Down(MigrationBuilder migrationBuilder)
    {
        migrationBuilder.Sql(@"DROP PROC UpdateProfilesCountry");
    }
}

This is a simple SQL code, that first checks if procedure exists and if so, it deletes it. Then it creates a new procedure with UpdateProfilesCountry name, which will update Country column for every Profile that phone number starts from 48.

When this migration will be run on the database, it will create UpdateProfilesCountry stored procedure as in my case.

Running the stored procedure

There is no dedicated method to run a stored procedure, so in the case where a stored procedure doesn’t return data, we can just invoke it as a raw SQL. This can be achieved like this:

await primeDbContext.Database.ExecuteSqlInterpolatedAsync(
    "UpdateProfilesCountry @p0",
    parameters: new[] { minimalProfileId.ToString() });

When I query the database for all numbers starting with 48, I will see that the country has been updated to Poland. This means that our procedure was executed successfully.

BTW. Don’t worry, those are fake data, generated with Bogus 🙂

Summary

Entity Framework Core can handle stored procedures quite well. There is no dedicated method to run the procedure, but it can be run as a standard raw SQL. However, when you’d like to query the database with a stored procedure, you must use a different approach. I have described that in a separate post: Select data with a stored procedure with Entity Framework Core 5.

What’s more, you can handle adding or updating stored procedures with EF Core migrations, which means that all work required can be done with EF Core.

All code mentioned here can be found on my GitHub, feel free to experiment with it.

Cheers!

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!