Pobranie danych za pomocę procedury składowanej w Entity Framework Core 5

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ą, która zwraca dane w Entity Framework Core 5? W moim ostatnim poście: Wykonanie procedury składowanej w Entity Framework Core pokazałem, jak uruchomić procedurę składowaną. Jednak pobranie danych to już zupełnie inna historia. Spójrzmy.

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 narzędzia globalnego EF Core:

dotnet ef migrations add spGetGuestsForDate 

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

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

            CREATE PROCEDURE [dbo].[GetGuestsForDate]
                @StartDate varchar(20)
            AS
            BEGIN
                SET NOCOUNT ON;
                SELECT p.Forename, p.Surname, p.TelNo, r.[From], r.[To], ro.Number As RoomNumber
                FROM Profiles p
                JOIN Reservations r ON p.ReservationId = p.ReservationId
                JOIN Rooms ro ON r.RoomId = ro.Id
                WHERE CAST([From] AS date) = CONVERT(date, @StartDate, 105)
            END";

        migrationBuilder.Sql(sql);
    }

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

Jest to prosty kod SQL, który najpierw sprawdza, czy procedura istnieje, a jeśli tak, usuwa ją. Następnie tworzy nową procedurę o nazwie GetGuestsForDate, która pobierze wszystkich przybywających gości danego dnia.

Kiedy migracja zostanie wykonana na bazie danych, ta procedura składowana będzie już istniała, co możemy zobaczyć tutaj:

Pobranie danych za pomocą procedury skłdowanej

Kiedy przyjrzysz się bliżej SQL, zauważysz, że spodziewamy się otrzymać listę gości z polami: Forename, Surname, TelNo, From, To and RoomNumber. Aby użyć procedury składowanej do zapytania bazy danych i zmapowania wyników na encje, musimy dodać odpowiednią encję. W moim przypadku dodam GuestArrival, który wygląda tak:

[Keyless]
public class GuestArrival
{
    public string Forename { get; set; }

    public string Surname { get; set; }

    public string TelNo { get; set; }

    public DateTime From { get; set; }

    public DateTime To { get; set; }

    public int RoomNumber { get; set; }
}

Ta klasa zawiera wszystkie kolumny, które chcę zmapować, a także ma atrybut [Keyless]. Encje bez klucza mają większość możliwości mapowania tak jak zwykłe encje, ale nie są śledzone pod kątem zmian w DbContext. Oznacza to również, że nie będziemy w stanie wykonać wstawiania, aktualizowania ani usuwania takiego obiektu.

Musimy również dodać DbSet do naszego kontekstu PrimeDbContext.

public class PrimeDbContext : DbContext
{
    public PrimeDbContext(DbContextOptions<PrimeDbContext> options)
        : base(options)
    {
    }

    // from stored procedures
    public virtual DbSet<GuestArrival> GuestArrivals { get; set; }
}

Teraz możemy iść dalej i użyć go. Oto, jak możemy wykonać tą procedurę składowaną:

var guests = primeDbContext.GuestArrivals.FromSqlInterpolated($"GetGuestsForDate '{date}'").ToList();

A jeśli umieszczę go w moim projekcie API, zmapuje wyniki na encje, to zobaczę taki wynik:

Wspomnę tutaj, ze używam parametru date w formacie dd-mm-rrrr, który przekazuję jako ciąg znaków do mojej procedury składowanej. Następnie w środku używam CONVERT(data, @StartDate, 105), gdzie 105 to format daty, który zamierzam konwertować. Więcej obsługiwanych formatów znajdziesz w tym artykule. Inną możliwością jest użycie parametru SqlParameter z typem danych Date, jednak formatowanie i przekazywanie daty może być kłopotliwe, więc wybrałem taki sposób.

Podsumowanie

Entity Framework Core może łatwo obsługiwać dane pobrane przez procedury składowane. Nie ma dedykowanej metody uruchamiania procedury, ale można ją uruchomić jako standardowy surowy kod SQL na kolekcji DbSet. Jeśli jednak interesuje Cię tylko wykonanie procedury składowanej, nie potrzebujesz do tego dedykowanego DbSet. Możesz sprawdzić szczegóły w moim poprzednim poście: Wykonanie procedury składowanej w Entity Framework Core.

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 opublikowany tutaj kod można znaleźć na moim GitHubie, możesz z nim eksperymentować do woli.

Pozdrawiam!

Select data with 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 that returns data in Entity Framework Core 5? In my last post: Execute a stored procedure with Entity Framework Core 5 I showed how to run a stored procedure, but selecting the data it’s a different kind of story. 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 spGetGuestsForDate 

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

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

            CREATE PROCEDURE [dbo].[GetGuestsForDate]
                @StartDate varchar(20)
            AS
            BEGIN
                SET NOCOUNT ON;
                SELECT p.Forename, p.Surname, p.TelNo, r.[From], r.[To], ro.Number As RoomNumber
                FROM Profiles p
                JOIN Reservations r ON p.ReservationId = p.ReservationId
                JOIN Rooms ro ON r.RoomId = ro.Id
                WHERE CAST([From] AS date) = CONVERT(date, @StartDate, 105)
            END";

        migrationBuilder.Sql(sql);
    }

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

This is a simple SQL code, that first checks if a procedure exists and if so, it deletes it. Then it creates a new procedure with the name GetGuestsForDate, which will get all arriving guests for a given date.

When the migration will be executed on the database, this stored procedure will be present, which we can see here:

Selecting data with a stored procedure

When you look closely at the SQL, you will notice that we expect to receive a list of guests with fields: Forename, Surname, TelNo, From, To and RoomNumber. In order to use the stored procedure to query the database and map results into entities, we need to add an appropriate entity. In my case I’ll add GuestArrival, that looks like this:

[Keyless]
public class GuestArrival
{
    public string Forename { get; set; }

    public string Surname { get; set; }

    public string TelNo { get; set; }

    public DateTime From { get; set; }

    public DateTime To { get; set; }

    public int RoomNumber { get; set; }
}

This class contains all columns that I’d like to map and also it has [Keyless] attribute. Keyless entities have most of the mapping capabilities as normal entities, but they are not tracked for changes in the DbContext. It also means that we won’t be able to perform insert, update, or delete on such entity.

We also need to add a DbSet to our PrimeDbContext.

public class PrimeDbContext : DbContext
{
    public PrimeDbContext(DbContextOptions<PrimeDbContext> options)
        : base(options)
    {
    }

    // from stored procedures
    public virtual DbSet<GuestArrival> GuestArrivals { get; set; }
}

Now we can go ahead and use it. Here is how we can accomplish that:

var guests = primeDbContext.GuestArrivals.FromSqlInterpolated($"GetGuestsForDate '{date}'").ToList();

And if I place it in my API project, it will map results to entities:

In here I’m using date parameter in dd-mm-yyyy format, which I pass as a string into my stored procedure. Then inside I use CONVERT(date, @StartDate, 105), where 105 is a date format I’m going to parse. You can find more supported formats in this article. It probably would be better to use SqlParameter class with Date datatype, but I couldn’t get it to work. 

Summary

Entity Framework Core can handle data selected by stored procedures easily. There is no dedicated method to run the procedure, but it can be run as a standard raw SQL on a DbSet. However, if you’re interested only in executing the stored procedure, you don’t need a DbSet for it. You can check out the details in my previous post: Execute 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!

 

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!

Scalanie migracji w Entity Framework Core 5

Podczas pracy z szybko rozwijającym się projektem zmiany zachodzą szybko nie tylko w kodzie projektu, ale także w schemacie bazy danych. Dzieje się tak zwłaszcza podczas pracy nad mikroserwisem od samego początku, kiedy często zmienia się jego przeznaczenie.

Jak działają migracje w EF Core 5

W migracjach Entity Framework Core 5 dodajemy migrację jako zmiany pomiędzy naszą klasą DbContext a istniejącą [nazwa DbContext] ModelSnapshot. Podczas generowania nowej migracji narzędzie CLI wygeneruje tylko różnice między tymi dwoma bytami i umieści je w dwóch metodach: Up i Down. W pierwszej nastąpi zmiana dotycząca zastosowania migracji, aw drugiej usunięcia migracji.

Po zastosowaniu migracji jej nazwa jest zapisywana w tabeli __EFMigrationsHistory.

Scalanie wielu migracji

Powiedzmy, że po wielokrotnej zmianie schematu na wczesnych etapach nasz projekt jest teraz stabilny. Mamy kilka migracji, które można by połączyć w jedną, która raz stworzyłaby model, bez wielu małych aktualizacji.

Scalanie kiedy możemy wszystko usunąć

Najłatwiejszym sposobem scalenia wszystkich migracji byłoby usunięcie wszystkiego! A dokładnie mam na myśli taki proces:

  • usuń katalog Migrations ze wszystkimi migracjami
  • wyczyść tabelę __EFMigrationHistory
  • usuń wszystkie tabele i inne obiekty bazy danych, które zostały dodane podczas migracji
  • utwórz nową migrację ze wszystkimi zmianami

Jest to drastyczny sposób scalania migracji, ponieważ utracimy wszystkie dane. Jest to jednak bardzo proste i może w niektórych przypadkach spełniać swoją rolę.

Scalanie, kiedy chcemy zachować dane

Kiedy chcemy zachować dane, nie możemy usunąć wszystkich już utworzonych obiektów bazy danych, ale możemy scalić pliki migracji w naszym kodzie. Zobaczmy, jak można to zrobić.

  • Usuń wszystkie skrypty migracji z folderu Migrations
  • Dodaj nową migrację za pomocą polecenia dotnet ef migrations add MergedMigration
  • Skopiuj cały plik i wyczyść obie metody Up i Down
  • Zaktualizuj bazę danych i zastosuj migrację MergedMigration za pomocą polecenia dotnet ef database update
  • Następnie zamień zawartość pliku MergedMigration na wcześniej wygenerowany kod

W rezultacie będziesz mieć tylko jeden plik migracji. W moim przykładzie tabela __EFMigrationHistory wygląda następująco.

A teraz w Visual Studio widzę tylko jedną migrację.

Zawiera ona zmiany ze wszystkich moich poprzednich migracji, które zostały scalone.

Zadziałało!

Wskazówka! Możesz również nazwać scaloną migrację tak jak pierwszą, która została już zastosowana, aby nie trzeba było aktualizować bazy danych i dodawać jej do tabeli __EFMigrationHistory.

Nie zadziała za każdym razem

Migracje możemy łatwo łączyć, gdy są stosowane tylko do kontrolowanej przez nas bazy danych. Ponadto ten proces nie będzie działać w środowiskach, w których nie zastosowano wszystkich scalonych migracji. Proces jest łatwy, ale są pewne rzeczy, które należy zwrócić uwagę.

Z drugiej strony, czy musimy zachować wszystkie migracje, nawet jeśli wiemy, że nigdy więcej ich nie uruchomimy? Nie sądzę. W tym miejscu byłoby wspaniale połączyć stare migracje, ale zostawić te najnowsze. Można to osiągnąć w bardzo podobny sposób.

  • Przywróć ostatnie N migracji lokalnie, jedna po drugiej i przenieś je gdzieś
  • Przywróć stan projektu w miejscu pasującym do migracji (git checkout w odpowiednim miejscu w historii)
  • Scal wszystkie istniejące migracje
  • Przywróć stan projekt do najnowszej wersji
  • Dodawaj po kolei zapisane najnowsze migracje

W takim przypadku zachowamy najnowsze migracje i utworzymy dużą migrację początkową, która byłaby zgodna z Twoim projektem. Jeśli wystąpi przypadek, w którym wszystkie migracje będą musiały zostać zastosowane do bazy danych, schemat bazy danych nie zostanie uszkodzony.

Podsumowanie

Scalanie migracji w Entity Framework Core 5 jest możliwe i mogę powiedzieć, że jest to zaskakująco łatwe. Obejmuje jednak proces automatycznego generowania i musisz sprawdzić, czy migracja scalona działa dokładnie tak samo, jak wszystkie migracje stosowane pojedynczo. Co więcej, istnieje więcej niż jeden sposób łączenia migracji i musisz wybrać ten, który jest dla Ciebie najbardziej odpowiedni.

Cały kod zamieszczony tutaj jest dostępny na moim GitHubie, więc możesz go pobrać i bawić się nim. Spójrz również na ten post, jak go uruchomić: PrimeHotel – jak uruchomić projekt.

Dzięki za czytanie i do zobaczenia ponownie 🙂

Merging migrations in Entity Framework Core 5

When working with a fast-evolving project changes happen rapidly not only to the project’s code but also to the database schema. It happens especially when working on a micro-service from the start when its purpose shifts.

How EF Core 5 migrations work

In Entity Framework Core 5 migrations we are adding a migration as a delta changes between our DbContext class and existing [DbContext name]ModelSnapshot. When generating new migration a CLI Tool will generate only the differences between those two and put them in two methods: Up and Down. In the first one, there will be a change to apply the migration and in the second one, to remove the migration.

After the migration is applied, its name is noted in the __EFMigrationsHistory table.

Merging multiple migrations 

Let’s say that after changing a schema multiple times in the early stages our project is now stable. We have a bunch of migrations that could be merged into one, that would create a model once, without many small updates. 

When we can remove everything

The easiest way to merge all migrations would be removing everything! What I mean is:

  • remove Migrations directory with all migrations
  • clear __EFMigrationHistory table
  • remove all tables and other database objects that were added via migrations
  • create new migration with all changes

This is a drastic way of merging migrations because we will lose all the data. However, it’s super simple and it might work in some cases.

When we need to preserve data

This means that we cannot remove all already created database objects, but we can merge migration files in our code. Let’s see how that can be done.

  1. Delete all migration scripts from Migrations folder
  2. Add a new migration with command dotnet ef migrations add MergedMigration
  3. Copy the entire file and clear both Up and Down methods
  4. Update the database and apply MergedMigration migration with command dotnet ef database update
  5. After that, replace the content of the MergedMigration file with earlier generated code

As a result, you will have only one migration file. In my example the __EFMigrationHistory table looks like this.

And now in the Visual Studio, I can see only on migration.

That contains changes from all my previous migrations merged together.

It worked!

Tip! You can also name you merged migration as the first one, that was already applied so that you wouldn’t have to update the database and add it to the __EFMigrationHistory table. 

It will not work in every case

We can easily merge migrations when they are applied only to the database that we control. I addition, it will not work for environments that don’t have all merged migrations applied. The process is easy, but there are certain things that need to be taken into account.

On the other hand, do we need to keep all migrations, even when we know that we won’t run them ever again? I don’t think so. At this point, it would be great to merge old migrations but leave the newest ones. It could be accomplished with a very similar process.

  1. Revert last N migrations locally, one by one, and move them somewhere
  2. Check out the project in the place matching the migrations
  3. Merge all existing migrations
  4. Check out the project in the newest state
  5. Add saved newest migrations, one by one

In this case, we will keep the newest migrations and create a big initial migration, that would be consistent with your project. If there will be a case, where all migrations would need to be applied to a database, the database schema wouldn’t be broken.

Summary

Merging migrations in Entity Framework Core 5 is possible and I can say it’s surprisingly easy. However, it involves an automatic generation process and you need to check if merged migration does exactly the same thing as all migrations applied one by one. Moreover, there is more than one way to merge migrations and you need to choose the one that is best for you.

All code posted here is available at my GitHub, so you can download it and play with it. Take a look also at this post on how to run it: PrimeHotel – how to run this project.

Thanks for reading and hopefully see you again 🙂

Dodanie migracji w Entity Framework Core 5 do projektu w .NET 5

Migracje bazy danych pomagają programiście w utrzymywaniu aktualności schematu bazy danych z kodem. Jest to podstawowy mechanizm, który utrzymuje zmiany w kodzie i stosuje je w bazie danych. Migracje Entity Framework Core 5 są przeznaczone do śledzenia klasy DbContext i generowania migracji podczas jej aktualizacji.

Instalowanie narzędzi

Aby dodać migracje EF Core, musisz mieć już skonfigurowane Entity Framework Core w projekcie. Możesz sprawdzić, jak przejść przez ten proces w tym poście: PrimeHotel – dodanie Entity Framework Core 5 w .NET 5

Najłatwiejszym sposobem dodawania migracji i zarządzania nimi jest użycie narzędzi .NET Core CLI, które powinieneś mieć już zainstalowane. Wpisz poniższe polecenie, aby to sprawdzić:

dotnet tool install --global dotnet-ef

Możesz także zaktualizować narzędzie po zainstalowaniu używając komendy:

Dodanie migracji

Dodanie pierwszej migracji nie różni się zbytnio od dodawania kolejnych. Musisz otworzyć okno terminala w lokalizacji projektu i wykonać polecenie:

dotnet ef migrations add InitialCreate

Kiedy to polecenie zostanie wykonane pomyślnie, wygeneruje katalog Migrations. Plik InitialCreate to indywidualna migracja w celu dopasowania do DbContext. PrimeDbContextModelSnapshot reprezentuje bieżący stan modelu. Jest dodawany do projektu podczas tworzenia pierwszej migracji i aktualizowany przy każdej kolejnej migracji. Umożliwia platformie migracji obliczenie zmian wymaganych w celu zaktualizowania bazy danych do modelu.

W pliku InitialCreate znajdziesz dwie metody: w Up i Down. Będą one reprezentować zmiany, kiedy migracja zostanie zastosowana i kiedy zostanie wycofana.

Wygenerowana migracja pozostanie taka, jaka jest. Nie jest to plik generowany automatycznie, który zostanie zaktualizowany w dalszej części procesu. Ta migracja została wygenerowana, abyś mógł rzucić okiem i sprawdzić, czy robi to, co powinna. Migrację możesz modyfikować zgodnie ze swoimi potrzebami, zatem nic nie stoi na przeszkodzie, aby wprowadzić własne ulepszenia.

Dodanie drugiej i kolejnych migracji nie będzie się właściwie niczym różniło od powyższego procesu. Wystarczy użyć polecenia dotnet migrations add <name> i zostanie wygenerowany odpowiedni plik.

Uruchamianie migracji ręcznie

W tym momencie możesz uruchomić migracje Entity Framework Core 5 i zaktualizować schemat bazy danych. Możesz to zrobić za pomocą następującego polecenia:

dotnet ef database update

Migracje bazy danych zostaną zastosowane, a wszystkie wykonane migracje zostaną odnotowane w tabeli __EFMigrationsHistory. Oto listing tej tabeli po kilku migracjach.

Uruchamianie migracji automatycznie

Byłoby wspaniale, gdyby nasze zmiany zostały sprawdzone i zastosowane przy każdym uruchomieniu projektu. Zobaczmy, jak możemy to osiągnąć. Przede wszystkim przejdźmy do pliku Startup.cs i utwórzmy metodę.

    private void UpgradeDatabase(IApplicationBuilder app)
    {
        using (var serviceScope = app.ApplicationServices.CreateScope())
        {
            var context = serviceScope.ServiceProvider.GetService<PrimeDbContext>();
            if (context != null && context.Database != null)
            {
                context.Database.Migrate();
            }
        }
    }

Ta metoda używa wbudowanego mechanizmu Dependency Injection, aby pobrać wystąpienie naszego PrimeDbContext i użyć go do uruchomienia migracji bazy danych. Tylko te, które nie zostały jeszcze zastosowane, zostaną uruchomione.

Następnie, w metodzie Configure, dodaj na końcu.

    UpgradeDatabase(app);

Dzięki tej konfiguracji mechanizmu aplikacja zaktualizuje używaną bazę danych, niezależnie od tego, czy jest uruchamiana lokalnie, czy też jest wdrażana i uruchamiana na serwerze produkcyjnym.

Podsumowanie

Migracje bazy danych ułatwiają aktualizowanie schematu bazy danych zgodnie ze zmianami kodu. Dodanie migracji Entity Framework Core 5 jest naturalnym krokiem, gdy masz Entity Framework Core. Wszystkie operacje można wykonać za pomocą narzędzi .NET Core CLI i bardzo prostych poleceń. Pamiętaj, że zawsze możesz edytować migracje przed ich zastosowaniem.

Cały kod opublikowany w tym poście jest dostępny na moim GitHub, więc możesz go dowolnie ściągać i modyfikować. Zerknij także na post jak uruchomić projekt PrimeHotel: PrimeHotel – jak uruchomić projekt

Dzięki za przeczytanie i powodzenia! 🙂

Adding Entity Framework Core 5 migrations to .NET 5 project

Database migrations help a developer to keep database schema up-to-date with the code. It is a core mechanism, that keeps changes in code and applies them in the database. Entity Framework Core 5 migrations are designed to keep track of DbContext class and generate migrations when you update it.

Installing the tools

To add EF Core migrations, you need to have Entity Framework Core already set up in your project. You can check how to go through that process in this post: PrimeHotel – adding Entity Framework Core 5 in .NET

The easiest way to add and manage migrations is to use .NET Core CLI tools, which you should have already installed. Type this command to check it out:

dotnet tool install --global dotnet-ef

You can also update the tool, once installed:

Adding a migration

Adding first migration doesn’t differ much from adding the next ones. You need to open a terminal window in the location of the project and execute the command:

dotnet ef migrations add InitialCreate

When this command executes successfully, it will generate a Migrations directory. InitialCreate file represents individual migration to match the DbContext. PrimeDbContextModelSnapshot represents the current state of the model. It is added to the project when the first migration is created and updated with each subsequent migration. It enables the migrations framework to calculate the changes required to bring the database up to date with the model.

In the InitialCreate file you will find two methods: Up and Down. Those will represent the changes when migration will be applied and when it would be rolled back.

Generated migration will stay the way it is. It’s not an auto-generated file that will be updated later in the process. This migration was generated for you to have a look and check if it does what it should. You can modify the migration according to your needs, nothing stops you from making some improvements.

Adding second and next migrations is a very similar process to the one presented above. You just need to use the command dotnet migrations add <name> add next migration will be generated.

Applying migrations manually

At this point, you can run your Entity Framework Core 5 migrations and update the database schema. You can do this via the following command:

dotnet ef database update

Database migrations will be applied and all executed migrations will be noted in the __EFMigrationsHistory. Here is the listing of this table after a couple of migrations.

Applying migrations automatically

It would be great if our changes were checked and applied on every project run. Let’s see how we can accomplish that. First of all, let’s go to Startup.cs file and create a method. 

    private void UpgradeDatabase(IApplicationBuilder app)
    {
        using (var serviceScope = app.ApplicationServices.CreateScope())
        {
            var context = serviceScope.ServiceProvider.GetService<PrimeDbContext>();
            if (context != null && context.Database != null)
            {
                context.Database.Migrate();
            }
        }
    }

This method will use built-in Dependency Injection mechanism to fetch an instance of our PrimeDbContext and use it to run database migrations. Only ones that were not applied yet will be run.

Now, in the Configure method add a line at the bottom.

    UpgradeDatabase(app);

With this mechanism set-up, the application will update the database it’s using, whether it is run locally, or deployed and run on a production server. 

Summary

Database migrations will help you keep database schema updated accordingly to the code changes. Adding an Entity Framework Core 5 migrations is a natural step when you have Entity Framework Core in place. All operations can be accomplished with .NET Core CLI tools and very simple commands. Remember that you can always edit migrations before applying them.

All code posted here is available at my GitHub, so you can download it freely. Take a look also at this post on how to run it: PrimeHotel – how to run this project.

Thanks for reading and good luck 🙂