在WinForms桌面应用中引入Dapper,可以让数据访问层保持极简,同时避开重型ORM的复杂配置。Dapper是一个基于ADO.NET的轻量级映射器,它通过扩展方法把SQL查询结果直接映射为强类型对象,非常适合不需要分布式部署的本地数据库场景。

一、环境准备与NuGet安装
新建一个Windows Forms App (.NET 6或更高)项目后,第一步是引入Dapper和数据库驱动。如果使用的是SQLite这种文件型数据库,还需要安装对应的ADO.NET提供程序。Dapper本身只有一个主包,不依赖任何特定数据库,真正决定连接方式的是底层的DbConnection实现。
在NuGet包管理器控制台执行以下命令即可完成安装。注意Dapper的版本应当与项目的目标框架兼容,一般选择稳定版即可。安装完成后,在代码中通过using Dapper;和using System.Data.SQLite;就能直接使用相关API。
// 在包管理器控制台执行 // Install-Package Dapper // Install-Package System.Data.SQLite.Core using Dapper; using System.Data.SQLite;
二、定义实体与数据库连接
假设我们要做一个简单的客户管理模块,先定义一个Customer类作为数据载体。Dapper默认按字段名和列名匹配,因此属性名称最好和数据库表中的列保持一致,或者通过SQL别名来映射。实体类应当是普通的POCO,不需要继承任何基类或打特殊标签。
连接字符串可以写在App.config里,也可以在代码里硬编码用于演示。对于WinForms程序,推荐把连接字符串放在配置文件中,方便后期更换数据库路径。下面代码展示了如何创建SQLite连接并查询所有客户记录。
public class Customer
{
public int Id { get; set; }
public string Name { get; set; }
public string Phone { get; set; }
}
public static class DbHelper
{
private static string ConnStr = "Data Source=local.db;Version=3;";
public static SQLiteConnection GetConnection()
{
return new SQLiteConnection(ConnStr);
}
}
三、在WinForms中异步调用Dapper
WinForms的UI线程如果被数据库查询阻塞,界面会直接失去响应。因此必须把Dapper的查询放到后台线程。Dapper提供了QueryAsync等异步方法,配合async/await可以轻松实现这一点。在事件处理方法中,用await把结果取回后,再赋值给DataGridView的数据源。
下面的示例演示了点击按钮后加载客户列表。这里使用ConfigureAwait(false)避免强制回到原始上下文造成死锁,但在WinForms中通常可以省略,因为需要回到UI线程更新控件。关键点在于:查询本身在线程池执行,绑定操作在UI线程执行。
private async void btnLoad_Click(object sender, EventArgs e)
{
using var conn = DbHelper.GetConnection();
var list = await conn.QueryAsync<Customer>("SELECT Id, Name, Phone FROM Customer");
dataGridView1.DataSource = list.ToList();
}
四、参数化查询与写入操作
直接拼接SQL字符串是WinForms新手最容易犯的错误,这会导致SQL注入风险。Dapper支持匿名对象作为参数,底层自动生成带@前缀的参数化命令。无论是插入、更新还是删除,都应当走参数化通道。
以下代码演示了新增一个客户并记录受影响行数。使用ExecuteAsync方法可以异步执行写操作,返回值是受影响的行数。如果需要在一次提交中完成多个步骤,还可以用BeginTransaction开启事务,保证数据一致性。
private async void btnAdd_Click(object sender, EventArgs e)
{
using var conn = DbHelper.GetConnection();
var sql = "INSERT INTO Customer(Name, Phone) VALUES(@Name, @Phone)";
var param = new { Name = txtName.Text, Phone = txtPhone.Text };
int rows = await conn.ExecuteAsync(sql, param);
MessageBox.Show("新增了" + rows + "条记录");
}
五、事务与批量处理
当界面上需要一次保存多行修改时,逐条提交会带来性能损耗。Dapper允许在一个打开的连接上开启事务,然后批量执行。事务能保证要么全部成功要么全部回滚,对桌面端断电极其有用。
下面示例把多个客户对象通过Execute一次性写入。虽然Dapper没有原生批量插入API,但借助事务和参数数组就能达到类似效果。注意SQLite在批量操作时应当关闭自动提交以提升速度。
private async void btnBatch_Click(object sender, EventArgs e)
{
using var conn = DbHelper.GetConnection();
await conn.OpenAsync();
using var tx = conn.BeginTransaction();
var sql = "INSERT INTO Customer(Name, Phone) VALUES(@Name, @Phone)";
var data = new[]
{
new { Name = "张三", Phone = "13800000001" },
new { Name = "李四", Phone = "13800000002" }
};
await conn.ExecuteAsync(sql, data, tx);
tx.Commit();
}
六、常见误区与小结
不少开发者把Dapper当成全自动框架,试图让它自动建表或做迁移,其实它只负责映射和执行SQL。表结构仍需手动维护或用其他工具生成。另外,在WinForms中切忌在构造函数里直接调数据库,否则设计器都会卡住。
总体来看,Dapper让WinForms数据层代码量降到最低,配合异步调用既能保持界面流畅,又能写出安全的参数化语句。对于中小规模桌面软件,它比Entity Framework更直白,也更容易排查慢查询。