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

来源:这里教程网 时间:2026-02-21 17:33:09 作者:

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.Json
Microsoft.Extensions.DependencyInjection

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

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

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

3. 在 Worker 类中使用 Dapper

通过构造函数注入

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

public class DataSyncWorker : BackgroundService
{
    private readonly IDbConnection _connection;
    private readonly ILogger<DataSyncWorker> _logger;
<pre class="brush:php;toolbar:false;">public DataSyncWorker(IDbConnection connection, ILogger<DataSyncWorker> 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<int>(
                "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);
<pre class="brush:php;toolbar:false;">var orders = await conn.QueryAsync<Order>(
    "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 请求有天然作用域,得自己兜底。

相关推荐