.NET unter Linux: Einfacher als es scheint

Veröffentlicht: 2022-08-16

Die Entwicklung von .NET-Lösungen unter Linux war schon immer eine Herausforderung, da Visual Studio von Microsoft Windows benötigt, um zu funktionieren. Nachdem ich an mehreren .NET-Projekten gearbeitet hatte, beschloss ich, die Grenzen der Entwicklung von .NET unter Linux auszutesten. Dieses einfache Tutorial konzentriert sich auf eine ASP.NET MVC-Anwendung mit SQL Server, um zu zeigen, wie elegant und effektiv die .NET-Entwicklung auf meinem bevorzugten Betriebssystem sein kann.

Entwicklungsumgebung

Zunächst müssen wir sicherstellen, dass die .NET-Tools und das SDK, die mit unserer speziellen Linux-Variante verbunden sind, mithilfe des Standardhandbuchs von Microsoft installiert werden.

Meine bevorzugte Entwicklungsumgebung besteht aus einer fensterbasierten integrierten Entwicklungsumgebung (IDE), einem leistungsstarken Datenbankverwaltungs- und Abfragetool, der Datenbank selbst und Tools zum Erstellen und Bereitstellen. Ich verwende die folgenden Tools, um eine solide Funktionalität zu erreichen und ein schönes Programmiererlebnis zu ermöglichen:

  • IDE: Visual Studio-Code
  • Datenbankverwaltungs- und Abfragetool: DBeaver
  • Datenbank: Microsoft SQL Server (Linux-Installation)
  • Build-Tools: .NET SDK Command Line Interface (CLI)
  • Virtuelle Maschine und Container: Docker

Stellen Sie sicher, dass diese Tools ordnungsgemäß installiert sind, bevor Sie mit unserer Beispielanwendung fortfahren.

Projekt Gerüst

In dieser Beispielanwendung werden wir die Entwicklung und Funktionalität von ASP.NET anhand einer Reihe von Anwendungsfällen für ein hypothetisches Bestandsverwaltungssystem für Schuhgeschäfte hervorheben. Wie bei jeder neuen .NET-Anwendung müssen wir eine Projektmappe erstellen und ihr dann ein Projekt hinzufügen. Wir können die .NET SDK CLI-Tools nutzen, um unsere neue Lösung zu rüsten:

 mkdir Shoestore && cd Shoestore dotnet new sln

Erstellen Sie als Nächstes ein ASP.NET-Projekt, das der Einfachheit halber eine explizite Hauptklasse enthält, da diese Projektstruktur ASP.NET-Entwicklern am vertrautesten ist. Lassen Sie uns unser Projekt mit dem MVC-Muster erstellen:

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

Fügen Sie als Nächstes das Projekt zur Projektmappe hinzu:

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

Wir haben jetzt eine Standardlösung und das darin enthaltene ASP.NET-Projekt. Bevor Sie fortfahren, stellen Sie sicher, dass alles erstellt wird:

 cd Shoestore.mvc/ dotnet restore dotnet build

Gute Entwicklungspraktiken ermutigen dazu, wichtige Dienste und die Anwendungslaufzeit in Docker-Containern zu platzieren, um die Bereitstellung und Portabilität zu verbessern. Lassen Sie uns daher einen einfachen Docker-Container erstellen, um unsere Anwendung zu unterstützen.

Anwendungsportabilität

Docker-Images verweisen normalerweise auf ein anderes übergeordnetes Docker-Image als akzeptierten Ausgangspunkt für grundlegende Anforderungen wie Betriebssysteme und grundlegende Lösungen, einschließlich Datenbanken. Erstellen Sie gemäß dieser bewährten Docker-Methode sowohl eine Dockerfile- als auch eine Docker Compose-Datei für die ordnungsgemäße Dienstkonfiguration, während Sie auf von Microsoft veröffentlichte übergeordnete Images verweisen. Wir verwenden Docker-Stufen, um unser Image klein zu halten. Phasen ermöglichen es uns, das .NET SDK beim Erstellen unserer Anwendung zu verwenden, sodass die ASP.NET-Laufzeit nur erforderlich ist, während unsere Anwendung ausgeführt wird.

Erstellen Sie die Shoestore.mvc Dockerfile mit folgendem Inhalt:

 # 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" ]

Als Nächstes erstellen wir die Datei docker-compose.yml im Stammverzeichnis unserer Lösung. Zunächst enthält es nur einen Verweis auf das .Dockerfile unseres Anwendungsdienstes:

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

Lassen Sie uns auch unsere Umgebung mit einer .dockerignore-Datei konfigurieren, um sicherzustellen, dass nur die Build-Artefakte in unser Image kopiert werden.

Nachdem unser Anwendungsdienst nun eingefügt und seine Ausführungsumgebung betriebsbereit ist, müssen wir unseren Datenbankdienst erstellen und ihn mit unserer Docker-Konfiguration verbinden.

Der Datenbankdienst

Das Hinzufügen von Microsoft SQL Server zu unserer Docker-Konfiguration ist unkompliziert, zumal wir ein von Microsoft bereitgestelltes Docker-Image verwenden, ohne es zu ändern. Fügen Sie den folgenden Konfigurationsblock am Ende der Datei docker-compose.yml , um die Datenbank zu konfigurieren:

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

Hier verhindert ACCEPT_EULA , dass die Installation angehalten wird, und unsere ports -Einstellung lässt den standardmäßigen SQL Server-Port ohne Übersetzung passieren. Damit enthält unsere Compose-Datei sowohl unseren Anwendungsdienst als auch unsere Datenbank.

Bevor wir den Anwendungscode anpassen, überprüfen wir, ob unsere Docker-Umgebung funktioniert:

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

Unter der Annahme, dass beim Start keine Fehler auftreten, sollte unsere unvollständige Beispielanwendung über einen Webbrowser unter der lokalen Adresse http://localhost:8080 verfügbar sein.

Tools zur Codegenerierung

Jetzt können wir uns auf den unterhaltsamen Teil konzentrieren: das Anpassen des Anwendungscodes und das Sicherstellen, dass die Anwendungsdaten in der Microsoft SQL Server-Datenbank bestehen bleiben. Wir verwenden sowohl das Entity Framework (EF)- als auch das .NET SDK-Tool, um die Anwendung mit der Datenbank zu verbinden und das Modell, die Ansicht, den Controller und die für EF erforderliche Konfiguration der Anwendung zu rüsten.

Bevor wir die benötigten Tools angeben können, müssen wir eine tool-manifest Datei erstellen:

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

Fügen Sie dieser Datei die EF- und SDK-Tools mit diesen einfachen Befehlen hinzu:

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

Um die ordnungsgemäße Installation dieser Tools zu überprüfen, führen dotnet ef . Wenn ein Einhorn erscheint, sind sie korrekt installiert. Führen Sie als Nächstes dotnet aspnet-codegenerator aus, um die ASP.NET-Tools zu testen. die Ausgabe sollte ein allgemeiner CLI-Nutzungsblock sein.

Jetzt können wir diese Tools verwenden, um unsere Anwendung zu erstellen.

MVC: Modell

Die erste Aufgabe beim Erstellen unserer Anwendung ist das Erstellen des Modells. Da dieses Modell später zur Datenbank hinzugefügt wird, werden wir die MS SQL Server- und EF-Pakete in unser Projekt aufnehmen:

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

Erstellen Sie als Nächstes ein EF-Datenbankkontextobjekt, das bestimmt, welche Modelle der Datenbank hinzugefügt werden, und ermöglicht unserem Code den einfachen Zugriff auf diese Daten und die Abfrage dieser Daten aus der Datenbank.

Erstellen Sie ein Data für den EF-spezifischen Code und erstellen Sie die Datei „ Data/ApplicationDBContext.cs “ mit folgendem Inhalt:

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

Konfigurieren Sie als Nächstes die Datenbankverbindungszeichenfolge, die mit den Anmeldeinformationen übereinstimmen muss, die wir in unserer Dockerfile konfiguriert haben. Legen Sie den Inhalt von Shoestore/Shoestore.mvc/appsettings.json wie folgt fest:

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

Nachdem die Datenbankverbindungszeichenfolge konfiguriert und der Datenbankkontext codiert ist, können wir die Main -Funktion unserer Anwendung codieren. Wir werden die Behandlung von Datenbankausnahmen einschließen, um das Debuggen des Systems zu vereinfachen. Da außerdem ein .NET-Fehler im generierten Code dazu führt, dass der Docker-Container unsere Ansichten falsch bereitstellt, müssen wir unserer Ansichtsdienstkonfiguration spezifischen Code hinzufügen. Dadurch werden die Dateipfade explizit auf unseren Anzeigeort in unserem Docker-Image festgelegt:

 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"); });

Springen Sie nach unten zur IsDevelopment if -Anweisung in derselben Datei, um einen Datenbankmigrationsendpunkt zu unserem System hinzuzufügen, wenn es sich im Entwicklungsmodus befindet. Fügen Sie eine else -Anweisung mit dem folgenden Code hinzu:

 // 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(); }

Führen Sie als Nächstes einen Schnelltest durch, um sicherzustellen, dass die neuen Pakete und die Änderungen am Quellcode korrekt kompiliert werden:

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

Lassen Sie uns nun das Modell mit unseren erforderlichen Feldern füllen, indem wir die Datei Shoestore.mvc\Models\Shoe.cs erstellen:

 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 generiert SQL basierend auf dem zugeordneten Modell, seiner Kontextdatei und jeglichem EF-Code in unserer Anwendung. SQL-Ergebnisse werden dann übersetzt und bei Bedarf an unseren Code zurückgegeben. Wenn wir unser Shoe -Modell zu unserem Datenbankkontext hinzufügen, weiß EF, wie es zwischen MS SQL Server und unserer Anwendung übersetzt. Lassen Sie uns dies in der Datenbankkontextdatei 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)); } }

Schließlich verwenden wir eine Datenbankmigrationsdatei, um unser Modell in die Datenbank zu bringen. Das EF-Tool erstellt eine für MS SQL Server spezifische Migrationsdatei basierend auf dem Datenbankkontext und dem zugehörigen Modell (d. h. Shoe ):

 cd Shoestore.mvc/ dotnet ef migrations add InitialCreate

Lassen Sie uns mit der Ausführung unserer Migration warten, bis wir einen Controller und eine Ansicht eingerichtet haben.

MVC: Controller und Ansicht

Wir erstellen unseren Controller mit dem ASP.NET-Codegenerierungstool. Dieses Tool ist sehr leistungsfähig, erfordert jedoch bestimmte Hilfsklassen. Verwenden Sie die Design -Style-Pakete für die grundlegende Controller-Struktur und ihre EF-Integration. Fügen wir diese Pakete hinzu:

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

Jetzt ist das Erstellen unseres Standard-Controllers so einfach wie das Aufrufen des folgenden Befehls:

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

Wenn der Codegenerator unseren Controller erstellt, erstellt er auch eine einfache Ansicht für diesen Controller. Nachdem unsere MVC-Grundlagen abgeschlossen sind, sind wir bereit, alles zum Laufen zu bringen.

Migrations- und Anwendungstest

EF-Migrationen sind normalerweise eine einfache Angelegenheit, aber wenn Docker involviert ist, wird der Prozess komplexer. Im nächsten Artikel unserer Serie werden wir den wunderbar verschlungenen Weg untersuchen, wie diese Migrationen in unserer Docker-Lösung funktionieren, aber im Moment möchten wir nur, dass unsere Migration ausgeführt wird.

Alle Konfigurations- und Migrationsdateien sind in unserem Repository enthalten. Lassen Sie uns das vollständige Projekt auf unseren lokalen Computer klonen und die Migration durchführen:

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

Der docker composer Vorgang erstellt unsere Anwendung, führt die Migration aus und startet unsere ASP.NET-Anwendung mit der .NET-Laufzeit. Um auf die laufende Lösung zuzugreifen, besuchen Sie http://localhost:8080/Shoes .

Obwohl unsere Anwendungsschnittstelle einfach ist, demonstriert sie die Funktionalität auf allen Ebenen, von der Ansicht bis hinunter zur Datenbank.

.NET unter Linux funktioniert einfach

Sehen Sie sich das vollständige Repository an, um einen Überblick über unsere Lösung zu erhalten. Der nächste Artikel behandelt unsere Migration im Detail sowie Tipps und Tricks, um unsere Docker-Images schlank zu machen.

.NET unter Linux ist mehr als nur ein Wunschtraum: Es ist eine praktikable Kombination aus Sprache, Laufzeit und Betriebssystem. Viele Entwickler, die mit Visual Studio aufgewachsen sind, haben möglicherweise keine umfassende Erfahrung mit der .NET-Befehlszeilenschnittstelle, aber diese Tools sind effektiv und leistungsstark.

Weiterführende Literatur im Toptal Engineering Blog:

  • Erstellen einer ASP.NET-Web-API mit ASP.NET Core
  • 8 Gründe, warum Microsoft Stack immer noch eine gute Wahl ist
  • So verbessern Sie die ASP.NET-App-Leistung in der Webfarm mit Caching
  • Ein Elasticsearch-Tutorial für .NET-Entwickler
  • Was ist Kubernetes? Ein Leitfaden für Containerisierung und Bereitstellung

Der Toptal Engineering Blog bedankt sich bei Henok Tsegaye für die Durchsicht der in diesem Artikel vorgestellten Codebeispiele.