Docker 化您的 Web API 与 SQL Server、Dapper 和 FluentMigrator

发布: (2025年12月7日 GMT+8 07:26)
4 min read
原文: Dev.to

Source: Dev.to

介绍

在第 1 和 2 部分,我们介绍了用于模式管理和 CI/CD 自动化的 FluentMigrator。在本部分,我们将 对整个堆栈进行 Docker 化——SQL Server、迁移以及基于 Dapper 的 Web API——从而只需一个命令即可启动完整的开发环境。

完整技术栈

组件技术目的
数据库SQL Server 2022数据存储
迁移FluentMigrator架构版本管理
数据访问Dapper高性能 SQL 映射
APIASP.NET Core 8HTTP 端点
编排Docker Compose容器管理

我们要构建的内容

┌─────────────────────────────────────────────────────────────┐
│                    docker compose up                        │
└─────────────────────────────────────────────────────────────┘

              ┌───────────────┼───────────────┐
              ▼               ▼               ▼
        ┌──────────┐   ┌──────────┐   ┌──────────┐
        │ sqlserver│   │db-migrate│   │product‑api│
        │  :1433   │   │ (runs    │   │  :5050   │
        │          │◄──│  once)   │──►│          │
        └──────────┘   └──────────┘   └──────────┘
              │               │               │
              └───────────────┴───────────────┘

                     productapi‑network

流程

  1. sqlserver 启动并等待健康状态。
  2. db‑migrate 运行 FluentMigrator 迁移(一次),随后退出。
  3. product‑api 仅在迁移成功后启动。

为什么要 Docker 化?

好处描述
一键设置docker compose up – 完成
环境一致性在你的机器、同事机器以及 CI/CD 中保持一致
无需本地安装无需 SQL Server、SDK
隔离不会与其他项目冲突
近似生产环境完全测试将要部署的内容

项目结构

ProductApi/
├── docker-compose.yml              # Orchestrates everything
├── src/
│   ├── ProductWebAPI/
│   │   ├── Dockerfile              # API container
│   │   ├── Controllers/
│   │   └── Services/
│   ├── ProductWebAPI.Database/
│   │   ├── Dockerfile              # Migration runner
│   │   └── Migrations/
│   └── CommonComps/
│       ├── Models/                 # Domain models
│       └── Repositories/          # Dapper implementations

快速回顾:本栈中的 Dapper

┌─────────────────────────────────────────────────────────────┐
│                    ProductsController                        │
│                    (HTTP endpoints)                          │
└─────────────────────────┬───────────────────────────────────┘

┌─────────────────────────▼───────────────────────────────────┐
│                    ProductService                            │
│                    (Business logic)                          │
└─────────────────────────┬───────────────────────────────────┘

┌─────────────────────────▼───────────────────────────────────┐
│                 ProductRepository (Dapper)                   │
│          Write SQL → Get C# objects back                     │
└─────────────────────────┬───────────────────────────────────┘

┌─────────────────────────▼───────────────────────────────────┐
│              SQL Server (FluentMigrator‑managed)             │
└─────────────────────────────────────────────────────────────┘

示例仓库

public class ProductRepository : IProductRepository
{
    private readonly string _connectionString;

    public ProductRepository(IConfiguration configuration)
    {
        _connectionString = configuration.GetConnectionString("DefaultConnection")
            ?? throw new ArgumentNullException("Connection string not found");
    }

    private IDbConnection CreateConnection() => new SqlConnection(_connectionString);

    public async Task GetByIdAsync(int id)
    {
        const string sql = @"
            SELECT p.*, c.*
            FROM Products p
            INNER JOIN Categories c ON p.CategoryId = c.Id
            WHERE p.Id = @Id";

        using var connection = CreateConnection();

        var result = await connection.QueryAsync(
            sql,
            (product, category) => { product.Category = category; return product; },
            new { Id = id },
            splitOn: "Id"
        );

        return result.FirstOrDefault();
    }

    public async Task CreateAsync(Product product)
    {
        const string sql = @"
            INSERT INTO Products (Name, SKU, Description, Price, CategoryId, IsActive, CreatedAt)
            OUTPUT INSERTED.Id
            VALUES (@Name, @SKU, @Description, @Price, @CategoryId, @IsActive, GETUTCDATE())";

        using var connection = CreateConnection();
        return await connection.ExecuteScalarAsync(sql, product);
    }
}

Dapper 关键模式

  • QueryAsync – 返回集合。
  • QueryFirstOrDefaultAsync – 返回单个项目或 null
  • ExecuteScalarAsync – 返回单个值(例如插入的 ID)。
  • ExecuteAsync – 返回受影响的行数。

为什么选择 Dapper?

  • ⚡ 接近原始 ADO.NET 的性能。
  • 💪 完全掌控 SQL。
  • 📦 轻量级(没有沉重的 ORM 开销)。

步骤 1:Web API 的 Dockerfile

File: src/ProductWebAPI/Dockerfile

# Use the official .NET 8 SDK image for building
FROM mcr.microsoft.com/dotnet
Back to Blog

相关文章

阅读更多 »

复选框 Aria TagHelper

介绍 了解如何使用自定义 TagHelper 初始化位于 ASP.NET Core 页面中的所有复选框输入。该 TagHelper 会评估 checked 属性……