.NET บน Linux: ง่ายกว่าที่คิด
เผยแพร่แล้ว: 2022-08-16การพัฒนาโซลูชัน .NET บน Linux เป็นเรื่องที่ท้าทายอยู่เสมอ เนื่องจาก Visual Studio ของ Microsoft ต้องการ Windows จึงจะใช้งานได้ หลังจากทำงานในโครงการ .NET หลายโครงการ ฉันตัดสินใจทดสอบขีดจำกัดของการพัฒนา .NET บน Linux บทช่วยสอนง่ายๆ นี้เน้นที่แอปพลิเคชัน ASP.NET MVC กับ SQL Server เพื่อแสดงให้เห็นว่าการพัฒนา .NET ที่หรูหราและมีประสิทธิภาพสามารถเป็นได้บนระบบปฏิบัติการที่ฉันต้องการ
การพัฒนาสภาพแวดล้อม
อันดับแรก เราต้องตรวจสอบให้แน่ใจว่าได้ติดตั้งเครื่องมือ .NET และ SDK ที่เกี่ยวข้องกับลินุกซ์ของเราโดยใช้คู่มือมาตรฐานของ Microsoft
สภาพแวดล้อมการพัฒนาที่ฉันชอบประกอบด้วยสภาพแวดล้อมการพัฒนาแบบบูรณาการแบบมีหน้าต่าง (IDE) การจัดการฐานข้อมูลและเครื่องมือสืบค้นข้อมูลที่มีประสิทธิภาพ ฐานข้อมูลเอง และเครื่องมือสำหรับการสร้างและการปรับใช้ ฉันใช้เครื่องมือต่อไปนี้เพื่อให้ได้ฟังก์ชันการทำงานที่มั่นคงและเปิดใช้งานประสบการณ์การเขียนโค้ดที่สวยงาม:
- IDE: รหัส Visual Studio
- การจัดการฐานข้อมูลและเครื่องมือสืบค้นข้อมูล: DBeaver
- ฐานข้อมูล: Microsoft SQL Server (การติดตั้ง Linux)
- เครื่องมือสร้าง: .NET SDK Command Line Interface (CLI)
- เครื่องเสมือนและคอนเทนเนอร์: Docker
ตรวจสอบให้แน่ใจว่าเครื่องมือเหล่านี้ได้รับการติดตั้งอย่างถูกต้องก่อนที่คุณจะดำเนินการกับแอปพลิเคชันตัวอย่างของเรา
โครงการนั่งร้าน
ในแอปพลิเคชันตัวอย่างนี้ เราจะเน้นย้ำถึงการพัฒนาและฟังก์ชันการทำงานของ ASP.NET ผ่านชุดกรณีการใช้งานสำหรับระบบการจัดการสินค้าคงคลังของร้านขายรองเท้าที่สมมติขึ้น เช่นเดียวกับแอปพลิเคชัน .NET ใหม่ เราจะต้องสร้างโซลูชันแล้วเพิ่มโครงการเข้าไป เราสามารถใช้ประโยชน์จากเครื่องมือ .NET SDK CLI เพื่อสร้างโซลูชันใหม่ของเรา:
mkdir Shoestore && cd Shoestore dotnet new sln
ถัดไป สร้างโครงการ ASP.NET ที่มีคลาสหลักที่ชัดเจนเพื่อความเรียบง่าย เนื่องจากโครงสร้างโครงการนี้คุ้นเคยกับนักพัฒนา ASP.NET มากที่สุด มาสร้างโครงการของเราโดยใช้รูปแบบ MVC:
mkdir Shoestore.mvc && cd Shoestore.mvc dotnet new mvc --use-program-main=true
ถัดไป เพิ่มโครงการในโซลูชัน:
# Go to the root of the solution cd .. dotnet sln add Shoestore.mvc/
ขณะนี้เรามีโซลูชันเริ่มต้นและโครงการ ASP.NET ที่มีอยู่ ก่อนดำเนินการต่อ ตรวจสอบให้แน่ใจว่าทุกอย่างสร้าง:
cd Shoestore.mvc/ dotnet restore dotnet build
แนวปฏิบัติในการพัฒนาที่ดีสนับสนุนให้ใส่บริการหลักและรันไทม์ของแอปพลิเคชันลงในคอนเทนเนอร์ Docker เพื่อการปรับใช้และการพกพาที่ดียิ่งขึ้น ดังนั้น เรามาสร้างคอนเทนเนอร์ Docker แบบง่าย ๆ เพื่อรองรับแอปพลิเคชันของเรากันเถอะ
การพกพาแอพพลิเคชั่น
โดยทั่วไปอิมเมจ Docker จะอ้างอิงอิมเมจ Docker หลักอื่นเป็นจุดเริ่มต้นที่ยอมรับสำหรับข้อกำหนดที่จำเป็น เช่น ระบบปฏิบัติการและโซลูชันพื้นฐาน รวมถึงฐานข้อมูล ตามแนวทางปฏิบัติที่ดีที่สุดของ Docker ให้สร้างทั้งไฟล์ Dockerfile และ Docker Compose สำหรับการกำหนดค่าบริการที่เหมาะสมในขณะที่อ้างอิงอิมเมจหลักที่ Microsoft เผยแพร่ เราจะใช้ขั้นตอน Docker เพื่อทำให้ภาพของเรามีขนาดเล็ก สเตจช่วยให้เราใช้ .NET SDK ขณะสร้างแอปพลิเคชันของเรา เพื่อให้จำเป็นต้องใช้รันไทม์ ASP.NET ในขณะที่แอปพลิเคชันของเราทำงานเท่านั้น
สร้าง Shoestore.mvc
Dockerfile ด้วยเนื้อหาต่อไปนี้:
# 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" ]
ต่อไป เราจะสร้างไฟล์ docker-compose.yml
ในไดเร็กทอรีรากของโซลูชันของเรา ในขั้นต้น จะมีเพียงการอ้างอิงถึง .Dockerfile
ของบริการแอปพลิเคชันของเรา:
# Shoestore/docker-compose.yml version: "3.9" services: web: build: context: . dockerfile: Shoestore.mvc/Dockerfile ports: - "8080:80"
มากำหนดค่าสภาพแวดล้อมของเราด้วยไฟล์ .dockerignore เพื่อให้แน่ใจว่ามีการคัดลอกเฉพาะสิ่งประดิษฐ์บิลด์ไปยังอิมเมจของเรา
ด้วยบริการแอปพลิเคชันของเราในขณะนี้และสภาพแวดล้อมการดำเนินการพร้อมที่จะรัน เราจำเป็นต้องสร้างบริการฐานข้อมูลของเราและเชื่อมต่อกับการกำหนดค่า Docker ของเรา
บริการฐานข้อมูล
การเพิ่ม Microsoft SQL Server ให้กับการกำหนดค่า Docker ของเรานั้นตรงไปตรงมา โดยเฉพาะอย่างยิ่งเนื่องจากเราใช้อิมเมจ Docker ที่ Microsoft จัดหาให้โดยไม่เปลี่ยนแปลง เพิ่มบล็อกการกำหนดค่าต่อไปนี้ที่ด้านล่างของไฟล์ docker-compose.yml
เพื่อกำหนดค่าฐานข้อมูล:
db: image: "mcr.microsoft.com/mssql/server" environment: SA_PASSWORD: "custom_password_123" ACCEPT_EULA: "Y" ports: - "1433:1433"
ที่นี่ ACCEPT_EULA
ป้องกันไม่ให้การติดตั้งหยุดชะงัก และการตั้งค่า ports
ของเราช่วยให้พอร์ต SQL Server เริ่มต้นส่งผ่านโดยไม่มีการแปล ด้วยเหตุนี้ ไฟล์ Compose ของเราจึงมีทั้งบริการแอปพลิเคชันและฐานข้อมูลของเรา
ก่อนปรับแต่งรหัสแอปพลิเคชัน ให้ตรวจสอบว่าสภาพแวดล้อม Docker ของเราใช้งานได้:
# From the root of the solution docker compose up --build
สมมติว่าไม่มีข้อผิดพลาดปรากฏขึ้นระหว่างการเริ่มต้น แอปพลิเคชันตัวอย่างที่ไม่สมบูรณ์ของเราควรพร้อมใช้งานผ่านเว็บเบราว์เซอร์ตามที่อยู่ในเครื่อง http://localhost:8080
เครื่องมือสร้างโค้ด
ตอนนี้ เรามาเน้นที่ส่วนที่สนุก: การปรับแต่งรหัสแอปพลิเคชัน และทำให้แน่ใจว่าข้อมูลแอปพลิเคชันยังคงอยู่ในฐานข้อมูล Microsoft SQL Server เราจะใช้ทั้งเครื่องมือ Entity Framework (EF) และ .NET SDK เพื่อเชื่อมต่อแอปพลิเคชันกับฐานข้อมูลและกำหนดรูปแบบแอปพลิเคชัน มุมมอง ตัวควบคุม และการกำหนดค่าที่ต้องใช้ EF
ก่อนที่เราจะระบุเครื่องมือที่เราต้องการได้ เราต้องสร้างไฟล์ tool-manifest
:
# From the root of the solution dotnet new tool-manifest
เพิ่มเครื่องมือ EF และ SDK ลงในไฟล์นี้ด้วยคำสั่งง่ายๆ เหล่านี้:
dotnet tool install dotnet-ef dotnet tool install dotnet-aspnet-codegenerator
หากต้องการตรวจสอบการติดตั้งที่เหมาะสมของเครื่องมือเหล่านี้ ให้รัน dotnet ef
หากยูนิคอร์นปรากฏขึ้น แสดงว่าได้รับการติดตั้งอย่างถูกต้อง ถัดไป เรียกใช้ dotnet aspnet-codegenerator
เพื่อทดสอบเครื่องมือ ASP.NET ผลลัพธ์ควรเป็นบล็อกการใช้งาน CLI ทั่วไป
ตอนนี้เราสามารถใช้เครื่องมือเหล่านี้เพื่อสร้างแอปพลิเคชันของเรา
MVC: รุ่น
งานแรกในการสร้างแอปพลิเคชันของเราคือการสร้างแบบจำลอง เนื่องจากโมเดลนี้จะถูกเพิ่มลงในฐานข้อมูลในภายหลัง เราจะรวมแพ็คเกจ MS SQL Server และ 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
ขั้นต่อไป ให้สร้างวัตถุบริบทฐานข้อมูล EF ที่กำหนดโมเดลที่จะเพิ่มลงในฐานข้อมูล และอนุญาตให้โค้ดของเราเข้าถึงและสืบค้นข้อมูลนั้นจากฐานข้อมูลได้อย่างง่ายดาย
สร้างไดเร็กทอรี Data
เพื่อจัดเก็บโค้ดเฉพาะของ EF และสร้างไฟล์ Data/ApplicationDBContext.cs
ที่มีเนื้อหาต่อไปนี้:
// Shoestore/Shoestore.mvc/Data/ApplicationDBContext.cs using Microsoft.EntityFrameworkCore; namespace Shoestore.mvc.Data; public class ApplicationDBContext : DbContext { public ApplicationDBContext(DbContextOptions<ApplicationDBContext> options):base(options){} }
ถัดไป กำหนดค่าสตริงการเชื่อมต่อฐานข้อมูล ซึ่งต้องตรงกับข้อมูลรับรองที่เรากำหนดค่าไว้ใน Dockerfile
ของเรา ตั้งค่าเนื้อหาของ Shoestore/Shoestore.mvc/appsettings.json
เป็นดังต่อไปนี้:
{ "Logging": { "LogLevel": { "Default": "Information", "Microsoft.AspNetCore": "Warning" } }, "AllowedHosts": "*", "ConnectionStrings": { "Shoestore": "Server=db;Database=master;User=sa;Password=custom_password_123;" } }
ด้วยการกำหนดค่าสตริงการเชื่อมต่อฐานข้อมูลและการเข้ารหัสบริบทของฐานข้อมูล เราพร้อมที่จะโค้ดฟังก์ชัน Main
ของแอปพลิเคชันของเราแล้ว เราจะรวมการจัดการข้อยกเว้นฐานข้อมูลเพื่อทำให้การดีบักระบบง่ายขึ้น นอกจากนี้ เนื่องจากข้อบกพร่องของ .NET ในโค้ดที่สร้างขึ้นทำให้คอนเทนเนอร์ Docker ให้บริการมุมมองของเราอย่างไม่ถูกต้อง เราจึงต้องเพิ่มโค้ดเฉพาะลงในการกำหนดค่าบริการมุมมองของเรา สิ่งนี้จะกำหนดเส้นทางของไฟล์ไปยังตำแหน่งมุมมองของเราอย่างชัดเจนในอิมเมจ 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"); });
ข้ามไปที่ IsDevelopment
if
statement ภายในไฟล์เดียวกันเพื่อเพิ่มจุดสิ้นสุดการย้ายฐานข้อมูลไปยังระบบของเราเมื่ออยู่ในโหมดการพัฒนา เพิ่มคำสั่ง else
ด้วยรหัสต่อไปนี้:
// 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(); }
ถัดไป ทำการทดสอบอย่างรวดเร็วเพื่อให้แน่ใจว่าแพ็คเกจใหม่และการแก้ไขซอร์สโค้ดคอมไพล์อย่างถูกต้อง:
// Go to mvc directory cd Shoestore.mvc dotnet restore dotnet build
ตอนนี้ มาเติมโมเดลด้วยฟิลด์ที่จำเป็นของเราโดยการสร้างไฟล์ 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 สร้าง SQL ตามโมเดลที่เกี่ยวข้อง ไฟล์บริบท และโค้ด EF ใดๆ ในแอปพลิเคชันของเรา ผลลัพธ์ของ SQL จะถูกแปลและส่งคืนไปยังโค้ดของเราตามความจำเป็น ถ้าเราเพิ่มโมเดล Shoe
ของเราในบริบทฐานข้อมูลของเรา EF จะทราบวิธีการแปลระหว่าง MS SQL Server และแอปพลิเคชันของเรา ลองทำสิ่งนี้ในไฟล์บริบทฐานข้อมูล 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)); } }
สุดท้าย เราจะใช้ไฟล์การโยกย้ายฐานข้อมูลเพื่อนำโมเดลของเราไปไว้ในฐานข้อมูล เครื่องมือ EF จะสร้างไฟล์การโยกย้ายเฉพาะสำหรับ MS SQL Server ตามบริบทฐานข้อมูลและรูปแบบที่เกี่ยวข้อง (เช่น Shoe
):
cd Shoestore.mvc/ dotnet ef migrations add InitialCreate
ให้หยุดการย้ายข้อมูลของเราจนกว่าเราจะมีตัวควบคุมและดูเข้าที่
MVC: ตัวควบคุมและมุมมอง
เราจะสร้างคอนโทรลเลอร์ของเราโดยใช้เครื่องมือสร้างโค้ด ASP.NET เครื่องมือนี้มีประสิทธิภาพมาก แต่ต้องมีคลาสตัวช่วยเฉพาะ ใช้แพ็คเกจรูปแบบ Design
สำหรับโครงสร้างคอนโทรลเลอร์พื้นฐานและการรวม EF มาเพิ่มแพ็คเกจเหล่านี้กัน:
cd Shoestore.mvc\ dotnet add package Microsoft.VisualStudio.Web.CodeGeneration.Design && \ dotnet add package Microsoft.EntityFrameworkCore.Design && \ dotnet restore
ตอนนี้ การสร้างตัวควบคุมเริ่มต้นของเรานั้นง่ายพอๆ กับการเรียกใช้คำสั่งต่อไปนี้:
cd Shoestore.mvc\ dotnet dotnet-aspnet-codegenerator controller \ -name ShoesController \ -m Shoe \ -dc ApplicationDBContext \ --relativeFolderPath Controllers \ --useDefaultLayout \ --referenceScriptLibraries
เมื่อตัวสร้างโค้ดสร้างคอนโทรลเลอร์ของเรา มันจะสร้างมุมมองที่เรียบง่ายสำหรับคอนโทรลเลอร์นั้นด้วย เมื่อรากฐาน MVC ของเราสมบูรณ์ เราก็พร้อมที่จะดำเนินการทุกอย่าง
การทดสอบการย้ายถิ่นและการสมัคร
การย้ายถิ่นของ EF มักเป็นเรื่องง่าย แต่เมื่อ Docker มีส่วนเกี่ยวข้อง กระบวนการก็จะซับซ้อนมากขึ้น ในบทความถัดไปในซีรีส์ของเรา เราจะสำรวจเส้นทางที่บิดเบี้ยวอย่างน่ามหัศจรรย์ในการทำให้การย้ายข้อมูลเหล่านั้นทำงานได้ในโซลูชัน Docker ของเรา แต่สำหรับตอนนี้ เราแค่ต้องการให้การโยกย้ายของเราทำงาน
ไฟล์การกำหนดค่าและการย้ายข้อมูลทั้งหมดรวมอยู่ในที่เก็บของเรา มาโคลนโปรเจ็กต์แบบเต็มไปยังเครื่องท้องถิ่นของเราและดำเนินการย้ายข้อมูล:
git clone https://github.com/theZetrax/dot-net-on-linux.git cd ./dot-net-on-linux docker composer up
การดำเนินการ docker composer
สร้างแอปพลิเคชันของเรา เรียกใช้การย้ายข้อมูล และเปิดแอปพลิเคชัน ASP.NET ของเราด้วยรันไทม์ .NET หากต้องการเข้าถึงโซลูชันที่ทำงานอยู่ ให้ไปที่ http://localhost:8080/Shoes
แม้ว่าส่วนต่อประสานแอปพลิเคชันของเรานั้นเรียบง่าย แต่ก็แสดงให้เห็นการทำงานผ่านทุกระดับ ตั้งแต่มุมมองจนถึงฐานข้อมูล
.NET บน Linux ใช้งานได้จริง
ดูที่เก็บแบบเต็มสำหรับภาพรวมของโซลูชันของเรา บทความถัดไปจะกล่าวถึงรายละเอียดการย้ายข้อมูลของเรา รวมถึงเคล็ดลับและเคล็ดลับในการทำให้อิมเมจ Docker ของเรามีขนาดเล็กลง
.NET บน Linux เป็นมากกว่าแค่ความฝัน: มันเป็นการผสมผสานระหว่างภาษา รันไทม์ และระบบปฏิบัติการ นักพัฒนาหลายคนที่เติบโตบน Visual Studio อาจไม่มีประสบการณ์ในการใช้ .NET CLI อย่างเต็มที่ แต่เครื่องมือเหล่านี้มีประสิทธิภาพและทรงพลัง
อ่านเพิ่มเติมในบล็อก Toptal Engineering:
- การสร้าง ASP.NET Web API ด้วย ASP.NET Core
- 8 เหตุผลทำไม Microsoft Stack ยังคงเป็นทางเลือกที่ดี
- วิธีปรับปรุงประสิทธิภาพของแอป ASP.NET ในเว็บฟาร์มด้วยการแคช
- บทช่วยสอน Elasticsearch สำหรับ .NET Developers
- Kubernetes คืออะไร? คู่มือการปรับใช้คอนเทนเนอร์และการปรับใช้
บล็อก Toptal Engineering ขอขอบคุณ Henok Tsegaye สำหรับการตรวจสอบตัวอย่างโค้ดที่นำเสนอในบทความนี้