Dockerizing ASP:NET核心Web API应用程序和SQL Server

189 阅读9分钟

Dockerizing a ASP.NET Core Web API App and SQL Server

微软开发了一个开源的、跨平台的框架,取代了旧的经典ASP.NET,称为ASP.NET Core。

该框架以构建现代的、基于云的、与互联网连接的网络应用程序和服务而闻名。

企业可以决定是在内部还是在云环境中部署应用程序。

Docker是允许开发者部署和运行容器的技术。它为应用程序提供了一个完整的运行环境,如操作系统和系统库,使其能够顺利地独立运行。

通过容器和对云友好的ASP.NET Core的结合,可以很容易地在云中运行高性能的.NET 服务。

本教程将讨论如何配置ASP.NET Core应用程序和SQL Server 2019在Docker容器上运行。

为了实现这一功能,我们将使用ASP.NET Core和SQL Server 2019构建一个电影列表应用程序。

前提条件

要继续学习本教程,你需要具备以下条件。

  1. 安装了桌面版[Docker]
  2. 安装了最新的[SQL Server Management Studio]
  3. 安装了[Visual Studio 2019]
  4. 熟练掌握[Docker命令]、[SQL Server命令]和[ASP.NET Core]语言。

设置SQL Server Docker镜像

我们将首先通过在终端执行以下命令,拉出最新的SQL Server 2019容器镜像。

$ docker pull mcr.microsoft.com/mssql/server:2019-latest

接下来,我们将使用以下命令继续运行我们的Docker容器。

$ docker run -e "ACCEPT_EULA=Y" -e "SA_PASSWORD=2Secure*Password2" -p 1450:1433 --name sqlserverdb -h mysqlserver -d mcr.microsoft.com/mssql/server:2019-latest

上面的命令包含以下内容。

  • --name:这是我们的容器的名称,在我们的例子中是sqlserverdb
  • 我们为我们的SQL服务器设置的密码是2Secure*Password2 。该密码必须足够强大,并满足最低密码要求。
  • -p 1450:1433:这些是我们要暴露的端口。

现在Docker容器已经启动并运行了。

使用SSMS连接SQL服务器

在这里,我们将使用SQL Server Management Studio工具来连接我们运行在Docker容器中的SQL服务器。

为了实现这一目标,我们将在SSMS连接窗口中输入以下细节。

  • 我们将给我们的服务器命名为localhost, 1450 。人们可以根据配置选择使用一个不同的IP地址。在我们的案例中,我们使用了localhost1433 作为我们配置的端口。
  • 对于SQL服务器的配置登录,我们使用了SA ,密码为2Secure*Password2

之后,我们点击Connect 按钮。它将确保我们与已经在我们的Docker容器中运行的SQL服务器连接。

ssms connect

一旦连接建立,我们就可以与数据库进行交互。

在ASP.NET Core应用程序中创建CRUD操作

我们将从启动Visual Studio开始。然后,我们选择ASP.NET Core Web App (Model-View-Controller) 模板,如下图所示。

new ASP project

接下来,我们将我们的ASP.NET Core应用程序命名为DockerSqlAsp 。确保不勾选Place solution and project in the same directory 这个名字下的复选框,如下图所示。

project name

然后,点击next ,我们继续配置我们的新项目。在这个阶段,我们将不勾选Enable Docker Support 的复选框。我们将点击create 按钮,如下图所示。

disable docker option

使用Entity Framework Core创建数据库

在这一节中,我们将在Docker容器中运行的SQL Server中创建一个新的数据库并将其命名为FilmDB

我们将只有一个名为Film 的表,我们将从ASP.NET Core应用程序中执行CRUD操作。

EF Core将处理这些操作。

前端ASP.NET Core应用程序将与运行在Docker上的后端SQL Server数据库交互,如下图所示。

asp sql interaction

在我们开始编码我们的应用程序之前,我们必须确保下面的NuGet 包已经被安装。

Microsoft.EntityFrameworkCore.Design
Microsoft.EntityFrameworkCore.Tools
Microsoft.EntityFrameworkCore.SqlServer

接下来,我们将在Models 目录内创建一个名为Film.cs 的新类,并粘贴以下代码。

using System.ComponentModel.DataAnnotations;

namespace DockerSqlAsp.Models {
    public class Film
    {
        [Required]
        public string ReleaseYear { get; set; }

        [Required]
        [Key]
        public int No { get; set; }

        [Required]
        [StringLength(50, ErrorMessage = "Movie name cannot exceed 50 characters.")]
        public string MovieName { get; set; }
    }
}

之后,我们将在同一目录下创建一个类,作为我们EF Core的数据库上下文,并将其命名为FilmContext.cs

然后,我们需要在该文件中粘贴以下代码。

using Microsoft.EntityFrameworkCore;

namespace DockerSqlAsp.Models {
    public class FilmContext : DbContext {
        public FilmContext(DbContextOptions<FilmContext> opt_Db) : base(opt_Db) {
        }
        public DbSet<Film> Film { get; set; }
    }
}

设置一个控制器

由于我们已经完成了我们的模型,我们可以在Controllers 目录中创建一个名为FileController.cs 的文件。

在这里,我们将定义在我们的应用程序中执行CRUD操作所需的动作方法,如下所示。

using System.Threading.Tasks;
using DockerSqlAsp.Models;
using Microsoft.AspNetCore.Mvc;
using System.Linq;

namespace DockerSqlAsp.Controllers {
    public class FilmController : Controller {
        private FilmContext ctx;
        public FilmController(FilmContext fctx) {
            ctx = fctx;
        }

        public IActionResult AddNew() {
            return View();
        }

        [HttpPost]
        public async Task<IActionResult> Rem(int id) {
            var del = ctx.Film.Where(b => b.No == id).FirstOrDefault();
            ctx.Remove(del);
            await ctx.SaveChangesAsync();
            return RedirectToAction(nameof(Index));
        }

        [HttpPost]
        public async Task<IActionResult> Modify(Film flm) {
            if (ModelState.IsValid)
            {
                ctx.Update(flm);
                await ctx.SaveChangesAsync();
                return RedirectToAction(nameof(Index));
            }
            else
                return View(flm);
        }

        [HttpPost]
        public async Task<IActionResult> AddNew(Film flm) {
            if (ModelState.IsValid) {
                ctx.Add(flm);
                await ctx.SaveChangesAsync();
                return RedirectToAction(nameof(Index));
            }
            else
                return View();
        }
        public IActionResult Modify(int id) {
            var del = ctx.Film.Where(b => b.No == id).FirstOrDefault();
            return View(del);
        }
        public IActionResult Index() {
            var flm = ctx.Film.ToList();
            return View(flm);
        }
    }
}

创建用户界面视图

由于我们已经完成了控制器,现在是时候为我们的应用程序创建视图了。

Views/Home目录下,我们将创建三个文件,并命名为AddNew.cshtml,Modify.cshtml, 和Index.cshtml 。这些视图将作为我们应用程序的用户界面。

我们首先对AddNew.cshtml 进行编码,如下图所示。

@model Film

@{
    ViewData["Title"] = "Add a New Movie";
}

<h1 class="bg-info text-white">Add a New Movie</h1>
<a class="btn btn-block btn-success" asp-action="Index">Show all Movies</a>

<div class="text-danger" asp-validation-summary="All"></div>

<form enctype="application/x-www-form-urlencoded" method="post">
    <div>
        <label>Movie Name</label>
        <input class="form-control-range" asp-for="MovieName" type="text" />
        <label>Release Year</label>
        <input class="form-control-range" asp-for="ReleaseYear" type="text" /><br />
        <input class="btn btn-secondary" value="Add New" type="submit" />
    </div>
</form>

接下来,我们对文件Modify.cshtml 进行编码,如下图所示。

@model Film

@{
    ViewData["Title"] = "Modify a Movie";
}

<h1 class="bg-info text-white">Modify a Movie</h1>
<a class="btn btn-block btn-success" asp-action="Index">Show all Movies</a>

<form method="post" enctype="multipart/form-data">
    <div class="form-group">
        <label asp-for="No">No.</label>
        <input class="form-control-range" type="text" asp-for="No" readonly />
    </div>
    <div class="form-group">
        <label asp-for="MovieName">Movie Name</label>
        <input class="form-control-range" type="text" asp-for="MovieName" />
    </div>
    <div class="form-group">
        <label asp-for="ReleaseYear">Release Year</label>
        <input class="form-control-range" type="text" asp-for="ReleaseYear" />
    </div>
    <button class="btn btn-secondary" type="submit">Modify a movie</button>
</form>

最后,我们对文件Index.cshtml 进行编码,如下图所示。

@model List<Film>

@{
    ViewData["Title"] = "Movies Release Years";
}

<h1 class="bg-info text-white">Movies Release Years</h1>
<a class="btn btn-block btn-success" asp-action="AddNew">Add a New Movie</a>
<div>
    <table class="table">
        <tr>
            <th>S#</th>
            <th>Movie Name</th>
            <th>Release Year</th>
            <th>Action</th>
            <th>Action</th>
        </tr>
        @foreach (Film flm in Model)
        {
            <tr>
                <td>@flm.No</td>
                <td>@flm.MovieName</td>
                <td>@flm.ReleaseYear</td>
                <td>
                    <a asp-route-id="@flm.No" class="btn btn-block btn-dark" asp-action="Modify">Edit</a>
                </td>
                <td>
                    <form asp-route-id="@flm.No" method="post" asp-action="Rem">
                        <input class="btn-warning btn btn-block" value=”Remove” type="submit" />
                    </form>
                </td>
            </tr>
        }
    </table>
</div>

执行EF Core迁移

我们将首先在项目解决方案资源管理器中找到并打开文件appsettings.json 。我们将创建一个连接字符串,将我们的前端应用程序连接到SQL Server数据库。

该文件将显示为下面的亮点。

{
  "ConnectionStrings": {
    "DefaultConnection": "Initial Catalog=FilmDB; Data Source=localhost,1450; Persist Security Info=True;User ID=SA;Password= 2Secure*Password2"
  }
}

数据源属性代表SQL Server地址和Docker容器上运行的端口。初始目录值代表数据库名称。

可以将数据源字段改为我们正在使用的机器的内部IP地址。

接下来,我们将导航到文件Startup.cs ,将数据库上下文作为服务添加到ConfigureServices 方法中。

public void ConfigureServices(IServiceCollection config_serv)
{
    config_serv.AddDbContext<FilmContext>(filmOpt =>
       filmOpt.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
    config_serv.AddControllersWithViews();
}

我们还需要执行EF Core迁移命令。为了实现这一点,我们将在Visual Studio中导航到包管理器控制台窗口,并运行以下命令。

$ add-migration Migration1
$ database-update

上述命令将在容器中已经运行的SQL Server上生成一个带有FilmDB 的数据库。

测试应用程序的功能

我们现在可以继续并测试我们的应用程序,因为我们尝试了CRUD操作。

我们将在Visual Studio中运行我们的ASP.NET Core应用程序。然后我们点击Add a New Movie 按钮,填写所需的细节,并点击Add New 按钮。

一个new movie 记录将被插入,如下所示。

add new movie

new movie

接下来,我们可以点击Edit 按钮,并测试该应用程序。预期的结果如下所示。

edit the movie

modify movie

之后,我们可以点击Remove 按钮,删除该记录。

delete movie

正如我们在上面看到的,我们的应用程序正在按预期工作;我们已经执行了CRUD操作。

为项目设置Docker Compose

接下来,我们将使用Docker Compose在Docker容器内运行ASP.NET Core应用程序和SQL Server 2019。

我们将首先在Visual Studio解决方案资源管理器中右键点击ASP.NET项目名称,并选择容器协调支持选项。

container orchestrator

在出现的弹出窗口中,我们将选择Docker Compose 选项。

docker compose

在接下来出现的窗口中,我们将设置目标操作系统为Linux。

target os

一个名称为docker-compose 的新项目将被创建。

之后,我们将找到Docker Compose的配置文件,名称为docker-compose.yml 。这个文件位于新项目的目录中。我们将编辑该文件,添加一个新的服务名称,名为sqldb

然后我们将指定我们之前下载的docker镜像,设置密码和端口。

sqldb:
  image: mcr.microsoft.com/mssql/server:2019-latest
  environment:
    - SA_PASSWORD=2Secure*Password2
    - ACCEPT_EULA=Y
  ports:
    - "1440:1433"

我们应该注意到端口的问题。对于这一部分,我们将为SQL服务器设置一个与之前不同的端口。这是因为我们将使用一个不同的SQL服务器。

接下来,我们将通过运行EF Core的迁移命令再次迁移数据库。

一旦,我们保存了docker-compose 配置文件,Docker将创建两个容器,一个用于ASP.NET Core应用程序,另一个用于运行SQL服务器。

ASP SQL Container interactions

然后,我们再次编辑位于文件appsettings.json 中的连接字符串。它将协助容纳新的SQL Server,通过编辑端口回到1450

{
  "ConnectionStrings": {
    "DefaultConnection": " Initial Catalog=FilmDB; Data Source=sqldb; Persist Security Info=True;User ID=SA;Password=2Secure*Password2"
  }
}

请注意,我们提供的DataSource值是sqldb ,而不是localhost, 1440 。这是因为sqldb 是我们在docker-compose 配置文件中的SQL Server的当前服务名称。

这使得容器之间可以使用它们的名字而不是IP地址进行交互。

运行EF Core迁移

我们将再次编辑数据库连接字符串,使用localhost, 1440 ,而不是sqldb

主要原因是,EF core需要知道正在进行迁移的数据库。

修改后的连接字符串将如下图所示。

{
  "ConnectionStrings": {
    "DefaultConnection": " Initial Catalog=FilmDB; Data Source=localhost,1440; Persist Security Info=True;User ID=SA;Password=2Secure*Password2"
  }
}

接下来,在包管理器的控制台窗口中,我们将执行下面的命令。

$ add-migration Migration2
$ database-update

一旦我们成功地迁移了数据库,我们将把连接字符串编辑回我们之前设定的值。

{
  "ConnectionStrings": {
    "DefaultConnection": " Initial Catalog=FilmDB; Data Source=sqldb; Persist Security Info=True;User ID=SA;Password=2Secure*Password2"
  }
}

最好记住,在数据库迁移过程中,SQL Server容器需要运行。

如果不是这样,那么就应该在Visual Studio中重建解决方案,以确保数据库迁移的成功。

我们将在Visual Studio中运行该应用程序,并通过执行CRUD操作来重新测试该应用程序。

总结

在本指南中,我们已经学会了如何为ASP.NET Core应用程序和SQL服务器创建一个Docker容器。

我们还使用了Docker Compose来同时运行两个容器并执行CRUD操作。

我们已经能够提取并使用SQL Server容器镜像来运行SQL Server容器。这一点至关重要,特别是对于那些不打算下载和安装SQL Server到开发环境中的开发者。