Dapper怎么在后台服务(Worker Service)中使用 Dapper集成Worker Service

Dapper 在 Worker Service 中需手动管理连接生命周期,推荐使用 Transient 注册或方法内 using 创建连接并显式 Open/Close。

Dapper 在 .NET 后台服务(Worker Service)中使用,和在 Web API 或控制台应用里基本一致,核心是连接管理、依赖注入、生命周期适配三点。Worker Service 没有 HTTP 请求上下文,所以不能依赖 `Scoped` 生命周期来自动释放连接,得自己把控。

1. 安装必要 NuGet 包

确保项目包含以下包:

  • Dapper(核心 ORM 工具)
  • 对应数据库驱动:如 SQL Server 用 Microsoft.Data.SqlClient,MySQL 用 MySqlConnector,PostgreSQL 用 Npgsql
  • 配置相关(可选但推荐):Microsoft.Extensions.Configuration.JsonMicrosoft.Extensions.DependencyInjection

2. 配置连接字符串与注册服务

Program.cs 中读取 appsettings.json 并注册连接工厂(推荐封装为单例或 transient):

var builder = Host.CreateApplicationBuilder(args);
builder.Configuration.AddJsonFile("appsettings.json");

builder.Services.AddSingleton(sp => { var connectionString = builder.Configuration.GetConnectionString("DefaultConnection"); return new SqlConnection(connectionString); // 或 MySqlConnection/NpgsqlConnection }); // 或更推荐:注册一个工厂方法,每次调用新建连接(避免跨线程复用) builder.Services.AddTransient(sp => { var connectionString = builder.Configuration.GetConnectionString("DefaultConnection"); var conn = new SqlConnection(connectionString); conn.Open(); // Worker 中常需立即打开,避免 Execute 时才 Open 导致异常 return conn; });

3. 在 Worker 类中使用 Dapper

通过构造函数注入 IDbConnection(注意:若注册为 Transient,每次注入都是新实例;若为 Singleton,务必手动管理 Open/Close,否则会出错):

public class DataSyncWorker : BackgroundService
{
    private readonly IDbConnection _connection;
    private readonly ILogger _logger;
public DataSyncWorker(IDbConnection connection, ILogger logger)
{
    _connection = connection;
    _logger = logger;
}

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
    while (!stoppingToken.IsCancellationRequested)
    {
        try
        {
            // ✅ 安全写法:确保连接已打开(尤其注册为 Transient 时)
            if (_connection.State != ConnectionState.Open)
                _connection.Open();

            var count = await _connection.QuerySingleAsync(
                "SELECT COUNT(*) FROM Orders WHERE Status = @Status",
                new { Status = "Pending" });

            _logger.LogInformation("待处理订单数: {Count}", count);

            // 执行更新等操作...
            await _connection.ExecuteAsync(
                "UPDATE Orders SET Status = 'Processed' WHERE Status = @Status",
                new { Status = "Pending" });
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "数据同步失败");
        }

        await Task.Delay(TimeSpan.FromMinutes(5), stoppingToken);
    }
}

}

⚠️ 注意:不要把连接设为 Singleton 并长期保持 Open 状态,容易引发连接池耗尽或超时断连。建议用 Transient + 每次操作前检查 Open 状态,或改用 using 块(见下条)。

4. 更稳妥的连接用法(推荐)

直接在方法内创建并释放连接,完全规避生命周期争议:

private async Task ProcessOrdersAsync(CancellationToken ct)
{
    var connectionString = _configuration.GetConnectionString("DefaultConnection");
    using var conn = new SqlConnection(connectionString);
    await conn.OpenAsync(ct);
var orders = await conn.QueryAsync(
    "SELECT * FROM Orders WHERE CreatedAt > @Since",
    new { Since = DateTime.UtcNow.AddHours(-1) });

foreach (var order in orders)
{
    await conn.ExecuteAsync(
        "UPDATE Orders SET Status = 'Handled' WHERE Id = @Id",
        new { order.Id });
}

}

这种写法清晰、安全、符合 Dapper 原生习惯,也适配 Worker 的长周期运行场景。

基本上就这些。不复杂但容易忽略的是连接状态管理和释放时机——Worker 不像 Web 请求有天然作用域,得自己兜底。