.NET w systemie Linux: prostsze niż się wydaje

Opublikowany: 2022-08-16

Tworzenie rozwiązań .NET w systemie Linux zawsze stanowiło wyzwanie, ponieważ Microsoft Visual Studio wymaga systemu Windows do działania. Po pracy nad kilkoma projektami .NET postanowiłem przetestować granice rozwoju .NET na Linuksie. Ten prosty samouczek koncentruje się na aplikacji ASP.NET MVC z SQL Server, aby pokazać, jak eleganckie i efektywne może być programowanie .NET w moim preferowanym systemie operacyjnym.

Środowisko programistyczne

Po pierwsze, musimy upewnić się, że narzędzia .NET i SDK związane z naszą konkretną odmianą Linuksa są instalowane przy użyciu standardowego przewodnika firmy Microsoft.

Moje preferowane środowisko programistyczne składa się z okienkowego zintegrowanego środowiska programistycznego (IDE), potężnego narzędzia do zarządzania bazą danych i zapytań, samej bazy danych oraz narzędzi do budowania i wdrażania. Używam następujących narzędzi, aby osiągnąć solidną funkcjonalność i zapewnić piękne wrażenia z kodowania:

  • IDE: kod programu Visual Studio
  • Narzędzie do zarządzania bazą danych i zapytań: DBeaver
  • Baza danych: Microsoft SQL Server (instalacja Linux)
  • Narzędzia kompilacji: interfejs wiersza poleceń .NET SDK (CLI)
  • Maszyna wirtualna i kontenery: Docker

Upewnij się, że te narzędzia są poprawnie zainstalowane, zanim zaczniesz korzystać z naszej przykładowej aplikacji.

Rusztowanie projektowe

W tej przykładowej aplikacji omówimy programowanie i funkcjonalność ASP.NET za pomocą szeregu przypadków użycia hipotetycznego systemu zarządzania zapasami sklepu obuwniczego. Podobnie jak w przypadku każdej nowej aplikacji .NET, musimy stworzyć rozwiązanie, a następnie dodać do niego projekt. Możemy wykorzystać narzędzia .NET SDK CLI do stworzenia szkieletu naszego nowego rozwiązania:

 mkdir Shoestore && cd Shoestore dotnet new sln

Następnie utwórz projekt ASP.NET zawierający jawną klasę główną dla uproszczenia, ponieważ ta struktura projektu jest najbardziej znana deweloperom ASP.NET. Stwórzmy nasz projekt używając wzorca MVC:

 mkdir Shoestore.mvc && cd Shoestore.mvc dotnet new mvc --use-program-main=true

Następnie dodaj projekt do rozwiązania:

 # Go to the root of the solution cd .. dotnet sln add Shoestore.mvc/

Mamy teraz rozwiązanie domyślne i zawarty w nim projekt ASP.NET. Zanim przejdziesz dalej, upewnij się, że wszystko się buduje:

 cd Shoestore.mvc/ dotnet restore dotnet build

Dobre praktyki programistyczne zachęcają do umieszczania kluczowych usług i środowiska uruchomieniowego aplikacji w kontenerach platformy Docker w celu poprawy wdrażania i przenośności. Dlatego stwórzmy prosty kontener Docker do obsługi naszej aplikacji.

Przenośność aplikacji

Obrazy platformy Docker zazwyczaj odwołują się do innego nadrzędnego obrazu platformy Docker jako akceptowanego punktu wyjścia dla podstawowych wymagań, takich jak system operacyjny i podstawowe rozwiązania, w tym bazy danych. Postępując zgodnie z najlepszym rozwiązaniem platformy Docker, utwórz zarówno plik Dockerfile, jak i plik Docker Compose, aby zapewnić prawidłową konfigurację usługi podczas odwoływania się do obrazów nadrzędnych opublikowanych przez firmę Microsoft. Użyjemy etapów Docker, aby nasz obraz był mały. Etapy umożliwiają nam korzystanie z zestawu SDK platformy .NET podczas tworzenia naszej aplikacji, dzięki czemu środowisko uruchomieniowe ASP.NET jest wymagane tylko podczas działania naszej aplikacji.

Utwórz Shoestore.mvc Shoesstore.mvc z następującą zawartością:

 # Shoestore\Shoestore.mvc\Dockerfile # Build stage FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build WORKDIR /shoestore COPY Shoestore.mvc/*.csproj ./ # Restore project packages RUN dotnet restore COPY Shoestore.mvc/* ./ # Create a release build RUN dotnet build -c Release -o /app/build # Run the application and make it available on port 80 FROM mcr.microsoft.com/dotnet/aspnet:6.0 WORKDIR /app EXPOSE 80 # Assets and views COPY Shoestore.mvc/Views ./Views COPY Shoestore.mvc/wwwroot ./wwwroot COPY --from=build /app/build ./ ENTRYPOINT [ "dotnet", "Shoestore.mvc.dll" ]

Następnie utworzymy plik docker-compose.yml w katalogu głównym naszego rozwiązania. Początkowo będzie zawierać tylko odniesienie do .Dockerfile naszej usługi aplikacji:

 # Shoestore/docker-compose.yml version: "3.9" services: web: build: context: . dockerfile: Shoestore.mvc/Dockerfile ports: - "8080:80"

Skonfigurujmy również nasze środowisko za pomocą pliku .dockerignore, aby upewnić się, że tylko artefakty kompilacji są kopiowane do naszego obrazu.

Gdy nasza usługa aplikacji jest już skrócona, a jej środowisko wykonawcze jest gotowe do uruchomienia, musimy utworzyć naszą usługę bazy danych i połączyć ją z naszą konfiguracją Dockera.

Usługa bazy danych

Dodanie Microsoft SQL Server do naszej konfiguracji platformy Docker jest proste, zwłaszcza że używamy obrazu platformy Docker dostarczonego przez firmę Microsoft bez jego zmiany. Dodaj następujący blok konfiguracyjny na dole pliku docker-compose.yml , aby skonfigurować bazę danych:

 db: image: "mcr.microsoft.com/mssql/server" environment: SA_PASSWORD: "custom_password_123" ACCEPT_EULA: "Y" ports: - "1433:1433"

W tym ACCEPT_EULA zapobiega zatrzymaniu instalacji, a nasze ustawienie ports pozwala na przejście domyślnego portu SQL Server bez translacji. Dzięki temu nasz plik Compose zawiera zarówno naszą usługę aplikacji, jak i bazę danych.

Przed dostosowaniem kodu aplikacji zweryfikujmy, czy nasze środowisko Docker działa:

 # From the root of the solution docker compose up --build

Zakładając, że podczas uruchamiania nie pojawiają się żadne błędy, nasza niekompletna przykładowa aplikacja powinna być dostępna przez przeglądarkę internetową pod adresem lokalnym http://localhost:8080 .

Narzędzia do generowania kodu

Teraz możemy skupić się na zabawnej części: dostosowaniu kodu aplikacji i upewnieniu się, że dane aplikacji pozostają w bazie danych Microsoft SQL Server. Użyjemy narzędzi Entity Framework (EF) i .NET SDK, aby połączyć aplikację z bazą danych i utworzyć szkielet modelu aplikacji, widoku, kontrolera i konfiguracji wymaganej przez EF.

Zanim będziemy mogli określić potrzebne narzędzia, musimy utworzyć plik tool-manifest :

 # From the root of the solution dotnet new tool-manifest

Dodaj narzędzia EF i SDK do tego pliku za pomocą tych prostych poleceń:

 dotnet tool install dotnet-ef dotnet tool install dotnet-aspnet-codegenerator

Aby zweryfikować poprawność instalacji tych narzędzi, uruchom dotnet ef . Jeśli pojawi się jednorożec, są poprawnie zainstalowane. Następnie uruchom dotnet aspnet-codegenerator aby przetestować narzędzia ASP.NET; dane wyjściowe powinny być ogólnym blokiem użycia CLI.

Teraz możemy wykorzystać te narzędzia do stworzenia naszej aplikacji.

MVC: Model

Pierwszym zadaniem w budowaniu naszej aplikacji jest stworzenie modelu. Ponieważ ten model zostanie później dodany do bazy danych, w naszym projekcie uwzględnimy pakiety MS SQL Server i EF:

 cd Shoestore.mvc/ dotnet add package Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore dotnet add package Microsoft.EntityFrameworkCore.SqlServer dotnet add package Microsoft.EntityFrameworkCore.Tools dotnet restore

Następnie utwórz obiekt kontekstu bazy danych EF, który określa, które modele są dodawane do bazy danych, i umożliwia naszemu kodowi łatwy dostęp do tych danych i wykonywanie zapytań z bazy danych.

Utwórz katalog Data do przechowywania kodu specyficznego dla EF i utwórz plik Data/ApplicationDBContext.cs z następującą zawartością:

 // Shoestore/Shoestore.mvc/Data/ApplicationDBContext.cs using Microsoft.EntityFrameworkCore; namespace Shoestore.mvc.Data; public class ApplicationDBContext : DbContext { public ApplicationDBContext(DbContextOptions<ApplicationDBContext> options):base(options){} }

Następnie skonfiguruj ciąg połączenia z bazą danych, który musi odpowiadać poświadczeniom skonfigurowanym w naszym Dockerfile . Ustaw zawartość Shoestore/Shoestore.mvc/appsettings.json na następującą:

 { "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*", "ConnectionStrings": { "Shoestore": "Server=db;Database=master;User=sa;Password=custom_password_123;" } }

Po skonfigurowaniu parametrów połączenia z bazą danych i zakodowaniu kontekstu bazy danych jesteśmy gotowi do kodowania funkcji Main naszej aplikacji. Dołączymy obsługę wyjątków bazy danych, aby uprościć debugowanie systemu. Ponadto, ponieważ błąd platformy .NET w wygenerowanym kodzie powoduje, że kontener Docker niepoprawnie obsługuje nasze widoki, będziemy musieli dodać określony kod do naszej konfiguracji usługi widoku. Spowoduje to jawne ustawienie ścieżek plików do lokalizacji naszego widoku w naszym obrazie Docker:

 using Microsoft.EntityFrameworkCore; using Shoestore.mvc.Data; namespace Shoestore.mvc; // ... public static void Main(string[] args) { var builder = WebApplication.CreateBuilder(args); // Associate our EF database context and configure it with our connection string var connectionString = builder.Configuration.GetConnectionString("Shoestore"); builder.Services.AddDbContext<ApplicationDBContext>( options => options.UseSqlServer(connectionString)); // Middleware to catch unhandled exceptions and display a stack trace builder.Services.AddDatabaseDeveloperPageExceptionFilter(); // Add services to the container. // ASP.NET has a known issue where the final built app doesn't know where the view // files are (in the Docker container). // The fix is to specifically add view locations. builder.Services .AddControllersWithViews() .AddRazorOptions(options => { options.ViewLocationFormats.Add("/{1}/{0}.cshtml"); options.ViewLocationFormats.Add("/Shared/{0}.cshtml"); });

Przejdź do instrukcji IsDevelopment if w tym samym pliku, aby dodać punkt końcowy migracji bazy danych do naszego systemu, gdy jest on w trybie programistycznym. Dodaj instrukcję else z następującym kodem:

 // Configure the HTTP request pipeline. if (!app.Environment.IsDevelopment()) { // Leave the contents of the if block alone. These are hidden for clarity. } else { app.UseMigrationsEndPoint(); }

Następnie przeprowadź szybki test, aby upewnić się, że nowe pakiety i zmiany w kodzie źródłowym skompilują się poprawnie:

 // Go to mvc directory cd Shoestore.mvc dotnet restore dotnet build

Teraz wypełnijmy model naszymi wymaganymi polami, tworząc Shoestore.mvc\Models\Shoe.cs :

 namespace Shoestore.mvc.Models; public class Shoe { public int ID { get; set; } public string? Name { get; set; } public int? Price { get; set; } public DateTime CreatedDate { get; set; } }

EF generuje kod SQL na podstawie skojarzonego modelu, jego pliku kontekstu i dowolnego kodu EF w naszej aplikacji. Wyniki SQL są następnie tłumaczone i w razie potrzeby zwracane do naszego kodu. Jeśli dodamy nasz model Shoe do naszego kontekstu bazy danych, EF będzie wiedział, jak tłumaczyć między MS SQL Server a naszą aplikacją. Zróbmy to w pliku kontekstu bazy danych Shoestore/Shoestore.mvc/Data/ApplicationDBContext.cs :

 using Microsoft.EntityFrameworkCore; using Shoestore.mvc.Models; namespace Shoestore.mvc.Data; public class ApplicationDBContext : DbContext { public ApplicationDBContext(DbContextOptions<ApplicationDBContext> options) : base(options) { } private DbSet<Shoe>? _shoe { get; set; } public DbSet<Shoe> Shoe { set => _shoe = value; get => _shoe ?? throw new InvalidOperationException("Uninitialized property" + nameof(Shoe)); } }

Na koniec użyjemy pliku migracji bazy danych, aby wprowadzić nasz model do bazy danych. Narzędzie EF tworzy plik migracji specyficzny dla MS SQL Server na podstawie kontekstu bazy danych i powiązanego z nim modelu (tj. Shoe ):

 cd Shoestore.mvc/ dotnet ef migrations add InitialCreate

Powstrzymaj się z uruchomieniem naszej migracji, dopóki nie będziemy mieli kontrolera i widoku.

MVC: kontroler i widok

Nasz kontroler stworzymy za pomocą narzędzia do generowania kodu ASP.NET. To narzędzie jest bardzo potężne, ale wymaga określonych klas pomocniczych. Użyj pakietów stylów Design dla podstawowej struktury kontrolera i jego integracji z EF. Dodajmy te pakiety:

 cd Shoestore.mvc\ dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design && \ dotnet add package Microsoft.EntityFrameworkCore.Design && \ dotnet restore

Teraz utworzenie naszego domyślnego kontrolera jest tak proste, jak wywołanie następującego polecenia:

 cd Shoestore.mvc\ dotnet dotnet-aspnet-codegenerator controller \ -name ShoesController \ -m Shoe \ -dc ApplicationDBContext \ --relativeFolderPath Controllers \ --useDefaultLayout \ --referenceScriptLibraries

Kiedy generator kodu tworzy nasz kontroler, tworzy również prosty widok dla tego kontrolera. Po ukończeniu naszych podstaw MVC jesteśmy gotowi, aby wszystko działało.

Test migracji i aplikacji

Migracje EF są zwykle prostą sprawą, ale gdy zaangażowany jest Docker, proces staje się bardziej złożony. W następnym artykule z naszej serii przyjrzymy się cudownie zawiłej ścieżce, aby te migracje działały w naszym rozwiązaniu Docker, ale na razie chcemy, aby nasza migracja działała.

Wszystkie pliki konfiguracyjne i migracyjne znajdują się w naszym repozytorium. Sklonujmy cały projekt na nasz lokalny komputer i wykonajmy migrację:

 git clone https://github.com/theZetrax/dot-net-on-linux.git cd ./dot-net-on-linux docker composer up

Operacja docker composer tworzy naszą aplikację, uruchamia migrację i uruchamia naszą aplikację ASP.NET ze środowiskiem wykonawczym .NET. Aby uzyskać dostęp do działającego rozwiązania, odwiedź stronę http://localhost:8080/Shoes .

Chociaż nasz interfejs aplikacji jest prosty, demonstruje funkcjonalność na wszystkich poziomach, od widoku do bazy danych.

.NET w systemie Linux po prostu działa

Zobacz pełne repozytorium, aby zapoznać się z naszym rozwiązaniem. W następnym artykule szczegółowo omówimy naszą migrację, a także porady i wskazówki, dzięki którym nasze obrazy Dockera będą odchudzone.

.NET na Linuksie to coś więcej niż mrzonka: to realna kombinacja języka, środowiska wykonawczego i systemu operacyjnego. Wielu programistów wychowanych w programie Visual Studio może nie mieć pełnego doświadczenia w korzystaniu z interfejsu .NET CLI, ale te narzędzia są skuteczne i wydajne.

Dalsza lektura na blogu Toptal Engineering:

  • Tworzenie internetowego interfejsu API ASP.NET za pomocą ASP.NET Core
  • 8 powodów, dla których stos Microsoft jest nadal opłacalnym wyborem
  • Jak poprawić wydajność aplikacji ASP.NET w farmie sieci Web za pomocą buforowania?
  • Samouczek Elasticsearch dla programistów .NET
  • Co to jest Kubernetes? Przewodnik po konteneryzacji i wdrażaniu

Blog Toptal Engineering wyraża wdzięczność firmie Henok Tsegaye za przejrzenie próbek kodu przedstawionych w tym artykule.