.NET pe Linux: mai simplu decât pare

Publicat: 2022-08-16

Dezvoltarea soluțiilor .NET pe Linux a fost întotdeauna o provocare, deoarece Visual Studio de la Microsoft necesită Windows pentru a funcționa. După ce am lucrat la mai multe proiecte .NET, am decis să testez limitele dezvoltării .NET pe Linux. Acest tutorial simplu se concentrează pe o aplicație ASP.NET MVC cu SQL Server pentru a arăta cât de elegantă și eficientă poate fi dezvoltarea .NET pe sistemul de operare preferat.

Mediu de dezvoltare

În primul rând, trebuie să ne asigurăm că instrumentele .NET și SDK asociate cu versiunea noastră specială de Linux sunt instalate folosind ghidul standard Microsoft.

Mediul meu de dezvoltare preferat constă dintr-un mediu de dezvoltare integrat cu ferestre (IDE), un instrument puternic de gestionare a bazelor de date și de interogare, baza de date în sine și instrumente pentru construirea și implementarea. Folosesc următoarele instrumente pentru a obține o funcționalitate solidă și pentru a permite o experiență frumoasă de codare:

  • IDE: Cod Visual Studio
  • Managementul bazei de date și instrument de interogare: DBeaver
  • Baza de date: Microsoft SQL Server (instalare Linux)
  • Instrumente de construcție: Interfață de linie de comandă (CLI) .NET SDK
  • Mașină virtuală și containere: Docker

Asigurați-vă că aceste instrumente sunt instalate corect înainte de a continua cu aplicația noastră exemplu.

Proiect Schela

În acest exemplu de aplicație, vom evidenția dezvoltarea și funcționalitatea ASP.NET printr-o serie de cazuri de utilizare pentru un sistem ipotetic de gestionare a inventarului unui magazin de pantofi. Ca și în cazul oricărei aplicații .NET noi, va trebui să creăm o soluție și apoi să îi adăugăm un proiect. Putem folosi instrumentele CLI .NET SDK pentru a construi noua noastră soluție:

 mkdir Shoestore && cd Shoestore dotnet new sln

Apoi, creați un proiect ASP.NET care să conțină o clasă principală explicită de dragul simplității, deoarece această structură de proiect este cea mai familiară dezvoltatorilor ASP.NET. Să ne creăm proiectul folosind modelul MVC:

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

Apoi, adăugați proiectul în soluție:

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

Acum avem o soluție implicită și proiectul ASP.NET conținut. Înainte de a continua, asigurați-vă că totul se construiește:

 cd Shoestore.mvc/ dotnet restore dotnet build

Buna practică de dezvoltare încurajează introducerea serviciilor cheie și a timpului de rulare a aplicației în containerele Docker pentru o implementare și portabilitate îmbunătățite. Prin urmare, să creăm un container Docker simplu pentru a susține aplicația noastră.

Portabilitatea aplicației

Imaginile Docker fac referire de obicei la o altă imagine Docker părinte ca punct de plecare acceptat pentru cerințe esențiale, cum ar fi SO și soluții de bază, inclusiv baze de date. Urmând această bună practică Docker, creați atât un fișier Dockerfile, cât și un fișier Docker Compose pentru configurarea corectă a serviciului, în timp ce faceți referire la imaginile părinte publicate de Microsoft. Vom folosi etapele Docker pentru a ne menține imaginea mică. Etapele ne permit să folosim .NET SDK în timp ce construim aplicația noastră, astfel încât runtime-ul ASP.NET este necesar doar în timp ce aplicația noastră rulează.

Creați Shoestore.mvc cu următorul conținut:

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

Apoi, vom crea fișierul docker-compose.yml în directorul rădăcină al soluției noastre. Inițial, va conține doar o referință la .Dockerfile al serviciului nostru de aplicații :

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

De asemenea, să ne configuram mediul cu un fișier .dockerignore pentru a ne asigura că numai artefactele de compilare sunt copiate în imaginea noastră.

Cu serviciul nostru de aplicații acum blocat și mediul său de execuție gata de rulare, trebuie să creăm serviciul nostru de baze de date și să îl conectăm la configurația noastră Docker.

Serviciul baze de date

Adăugarea Microsoft SQL Server la configurația noastră Docker este simplă, mai ales că folosim o imagine Docker furnizată de Microsoft fără a o modifica. Adăugați următorul bloc de configurare în partea de jos a fișierului docker-compose.yml pentru a configura baza de date:

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

Aici, ACCEPT_EULA împiedică oprirea instalării, iar setarea noastră de ports lasă portul implicit SQL Server să treacă fără traducere. Prin urmare, fișierul nostru Compose include atât serviciul nostru de aplicație, cât și baza de date.

Înainte de a personaliza codul aplicației, să verificăm dacă mediul nostru Docker funcționează:

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

Presupunând că nu apar erori în timpul pornirii, aplicația noastră de probă incompletă ar trebui să fie disponibilă printr-un browser web la adresa locală http://localhost:8080 .

Instrumente de generare a codului

Acum ajungem să ne concentrăm pe partea distractivă: personalizarea codului aplicației și asigurarea faptului că datele aplicației persistă în baza de date Microsoft SQL Server. Vom folosi atât instrumentele Entity Framework (EF) cât și .NET SDK pentru a conecta aplicația la baza de date și pentru a stabili modelul aplicației, vizualizarea, controlerul și configurația necesară EF.

Înainte de a putea specifica instrumentele de care avem nevoie, trebuie să creăm un fișier tool-manifest :

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

Adăugați instrumentele EF și SDK la acest fișier cu aceste comenzi simple:

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

Pentru a verifica instalarea corectă a acestor instrumente, rulați dotnet ef . Dacă apare un unicorn, acestea sunt instalate corect. Apoi, rulați dotnet aspnet-codegenerator pentru a testa instrumentele ASP.NET; ieșirea ar trebui să fie un bloc general de utilizare CLI.

Acum putem folosi aceste instrumente pentru a crea aplicația noastră.

MVC: Model

Prima sarcină în construirea aplicației noastre este crearea modelului. Deoarece acest model va fi adăugat la baza de date mai târziu, vom include pachetele MS SQL Server și EF în proiectul nostru:

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

Apoi, creați un obiect context al bazei de date EF care determină ce modele sunt adăugate la baza de date și permite codului nostru să acceseze și să interogă cu ușurință acele date din baza de date.

Creați un director de Data pentru a găzdui codul specific EF și creați fișierul Data/ApplicationDBContext.cs cu următorul conținut:

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

Apoi, configurați șirul de conexiune la baza de date, care trebuie să se potrivească cu acreditările pe care le-am configurat în fișierul nostru Dockerfile . Setați conținutul Shoestore/Shoestore.mvc/appsettings.json la următoarele:

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

Cu șirul de conexiune la baza de date configurat și contextul bazei de date codat, suntem gata să codificăm funcția Main a aplicației noastre. Vom include gestionarea excepțiilor bazei de date pentru a simplifica depanarea sistemului. În plus, deoarece o eroare .NET în codul generat face ca containerul Docker să difuzeze incorect vizualizările noastre, va trebui să adăugăm un anumit cod la configurația serviciului nostru de vizualizare. Acest lucru va seta în mod explicit căile fișierelor către locația noastră de vizualizare din imaginea noastră 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"); });

Treceți în jos la IsDevelopment if din același fișier pentru a adăuga un punct final de migrare a bazei de date la sistemul nostru atunci când este în modul de dezvoltare. Adăugați o declarație else cu următorul cod:

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

Apoi, executați un test rapid pentru a vă asigura că noile pachete și editările codului sursă sunt compilate corect:

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

Acum, să populăm modelul cu câmpurile noastre obligatorii creând 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 generează SQL pe baza modelului asociat, a fișierului său context și a oricărui cod EF din aplicația noastră. Rezultatele SQL sunt apoi traduse și returnate la codul nostru, după cum este necesar. Dacă adăugăm modelul nostru Shoe în contextul bazei de date, EF va ști cum să traducă între MS SQL Server și aplicația noastră. Să facem asta în fișierul context al bazei de date, 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)); } }

În cele din urmă, vom folosi un fișier de migrare a bazei de date pentru a introduce modelul nostru în baza de date. Instrumentul EF creează un fișier de migrare specific pentru MS SQL Server pe baza contextului bazei de date și a modelului asociat acestuia (de exemplu, Shoe ):

 cd Shoestore.mvc/ dotnet ef migrations add InitialCreate

Să amânăm să rulăm migrarea până când avem un controler și o vizualizare la locul lor.

MVC: Controller și vizualizare

Ne vom crea controlerul folosind instrumentul de generare a codului ASP.NET. Acest instrument este foarte puternic, dar necesită clase de ajutor specifice. Utilizați pachetele de stil Design pentru structura de bază a controlerului și integrarea sa EF. Să adăugăm aceste pachete:

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

Acum, crearea controlerului nostru implicit este la fel de simplă ca invocarea următoarei comenzi:

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

Când generatorul de cod creează controlerul nostru, creează și o vizualizare simplă pentru acel controler. Cu bazele noastre MVC complete, suntem pregătiți să punem totul în funcțiune.

Test de migrare și aplicație

Migrațiile EF sunt de obicei o chestiune simplă, dar atunci când este implicat Docker, procesul devine mai complex. În următorul articol din seria noastră, vom explora calea minunat de întortocheată pentru a face aceste migrări să funcționeze în soluția noastră Docker, dar pentru moment, vrem doar ca migrarea noastră să ruleze.

Toate fișierele de configurare și migrare sunt incluse în depozitul nostru. Să clonăm proiectul complet pe mașina noastră locală și să efectuăm migrarea:

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

Operația de docker composer creează aplicația noastră, rulează migrarea și lansează aplicația noastră ASP.NET cu runtime .NET. Pentru a accesa soluția de rulare, vizitați http://localhost:8080/Shoes .

Deși interfața aplicației noastre este simplă, ea demonstrează funcționalitatea prin toate nivelurile, de la vizualizare până la baza de date.

.NET pe Linux Just Works

Consultați depozitul complet pentru o prezentare generală a soluției noastre. Următorul articol va acoperi în detaliu migrarea noastră, precum și sfaturi și trucuri pentru a face imaginile noastre Docker mai slabe.

.NET pe Linux este mai mult decât un vis: este o combinație viabilă de limbaj, timp de rulare și sistem de operare. Este posibil ca mulți dezvoltatori crescuți pe Visual Studio să nu aibă experiență în utilizarea .NET CLI la maximum, dar aceste instrumente sunt eficiente și puternice.

Citiți suplimentare pe blogul Toptal Engineering:

  • Construirea unui API Web ASP.NET cu ASP.NET Core
  • 8 motive pentru care Microsoft Stack este încă o alegere viabilă
  • Cum se îmbunătățește performanța aplicației ASP.NET în ferma web cu cache
  • Un tutorial Elasticsearch pentru dezvoltatorii .NET
  • Ce este Kubernetes? Un ghid pentru containerizare și implementare

Blogul Toptal Engineering își exprimă recunoștința lui Henok Tsegaye pentru revizuirea mostrelor de cod prezentate în acest articol.