Linux 上的 .NET:比看起来简单

已发表: 2022-08-16

在 Linux 上开发 .NET 解决方案一直具有挑战性,因为 Microsoft 的 Visual Studio 需要 Windows 才能工作。 在完成了几个 .NET 项目之后,我决定在 Linux 上测试 .NET 的开发极限。 这个简单的教程侧重于一个带有 SQL Server 的 ASP.NET MVC 应用程序,以展示在我首选的操作系统上进行 .NET 开发是多么优雅和有效。

开发环境

首先,我们必须确保使用 Microsoft 的标准指南安装与我们的特定 Linux 风格相关的 .NET 工具和 SDK。

我首选的开发环境包括一个窗口集成开发环境 (IDE)、一个强大的数据库管理和查询工具、数据库本身以及用于构建和部署的工具。 我使用以下工具来实现可靠的功能并实现美妙的编码体验:

  • IDE:Visual Studio 代码
  • 数据库管理和查询工具:DBeaver
  • 数据库:Microsoft SQL Server(Linux 安装)
  • 构建工具:.NET SDK 命令行界面 (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 配置中很简单,尤其是因为我们使用的是 Microsoft 提供的 Docker 映像而不更改它。 将以下配置块添加到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的 Web 浏览器访问。

代码生成工具

现在我们开始关注有趣的部分:自定义应用程序代码并确保应用程序数据保留在 Microsoft SQL Server 数据库中。 我们将使用实体框架 (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语句,以便在系统处于开发模式时将数据库迁移端点添加到我们的系统。 使用以下代码添加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 根据关联模型、其上下文文件以及我们应用程序中的任何 EF 代码生成 SQL。 然后根据需要翻译 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 工具根据数据库上下文及其关联模型(即Shoe )创建特定于 MS SQL Server 的迁移文件:

 cd Shoestore.mvc/ dotnet ef migrations add InitialCreate

让我们推迟运行迁移,直到我们有一个控制器和视图到位。

MVC:控制器和视图

我们将使用 ASP.NET 代码生成工具创建我们的控制器。 这个工具非常强大,但需要特定的帮助类。 对基本控制器结构及其 EF 集成使用Design风格的包。 让我们添加这些包:

 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操作构建我们的应用程序,运行迁移,并使用 .NET 运行时启动我们的 ASP.NET 应用程序。 要访问正在运行的解决方案,请访问http://localhost:8080/Shoes

尽管我们的应用程序界面很简单,但它展示了从视图到数据库的所有层级的功能。

Linux 上的 .NET 可以正常工作

有关我们解决方案的概述,请参阅完整的存储库。 下一篇文章将详细介绍我们的迁移,以及使我们的 Docker 镜像更精简的提示和技巧。

Linux 上的 .NET 不仅仅是一个白日梦:它是一种可行的语言、运行时和操作系统的组合。 许多在 Visual Studio 上长大的开发人员可能没有充分使用 .NET CLI 的经验,但这些工具既有效又强大。

进一步阅读 Toptal 工程博客:

  • 使用 ASP.NET Core 构建 ASP.NET Web API
  • Microsoft Stack 仍然是可行选择的 8 个原因
  • 如何通过缓存提高 Web Farm 中的 ASP.NET 应用程序性能
  • .NET 开发人员的 Elasticsearch 教程
  • 什么是 Kubernetes? 容器化和部署指南

Toptal 工程博客对 Henok Tsegaye 对本文中提供的代码示例的审阅表示感谢。