Dapper 是一个开源的、轻量级的 ORM 框架,性能出色,关于它的特点这里就不多说,网上的文章都在不厌其烦地提。个人比较喜欢它基于 SQL 语句的方式。本博文通过在 WinForm 界面下使用 Dapper 对数据库表进行增删改查操作,比较详细地讲解 Dapper 的用法,以及如何在 WinForm 下以绑定的方式来操作数据。

开发环境:

  • Microsoft Visual Studio Community 2019
  • Database: SQL Server 2019 Express

Dapper 的安装

新建一个 Windows Forms App,通过菜单: Tools -> NuGet Package Manager -> Manage NuGet packages for Solution,打开 第三方 package 的浏览界面,在搜索框中输入 Dapper,选中右边的项目,然后点击 Install 按钮即可以安装。

准备数据

本文的示例数据是通过 https://mockaroo.com/ 网站提供的模拟数据生成。我利用该网站生成了 employees 表的模拟数据,并且将创建表的 SQL 语句和创建模拟数据的 SQL 语句下载为 sql 脚本。示例数据我将和本文的源码一起上传到 Github,供小伙伴们参考。employees 表结构如下:

Dapper ORM 使用教程

连接字符串

在 .Net 平台编写数据库程序,都需要针对不同数据库的连接字符串,我使用的是 sql server express,连接字符串如下,sql server 其它版本类似。

Dapper ORM 使用教程
如果使用的数据库的用户名和密码连接,连接字符串为:

Dapper ORM 使用教程

Entity 类

Entity 类,或者称为 Model 类,主要是将表的字段映射为属性。在项目中新建一个 Employee 类,代码如下:

Dapper ORM 使用教程

Service 类

在项目中新建 EmployeeService 类。我们先编写一个 ListAll() 方法,该方法能查询出 employees 表的所有数据,代码如下:

Dapper ORM 使用教程
要点:

  • 方法返回 IList 接口,该接口支持泛型。这是一个最常规的处理方式,展示层可以直接将控件绑定到 IList
  • IDbConnection 是 Ado.Net 的接口类型,Dapper 的方法都是基于该接口类型实现了扩展方法
  • 因为本例中后台数据库为 sql server / sql server express,所以实际的连接对象为 System.Data.SqlClient.SqlConnection 对象
  • IDbConnectionQuery<T>() 方法,返回值类型为 IEnumerable<T>, 通过 ToList() 方法转换为 List<T>

完成一个方法后,可以对该方法进行单元测试。在解决方案 (Solution) 中新建一个 Unit Test Project,添加对 DapperDemo 工程的引用。编写 TestListAll() 方法的代码如下:

Dapper ORM 使用教程

接下来在 EmployeeService 类中编写一个根据 name 来查找对应 employees 的方法,以展示在 Dapper 中处理参数化查询的方法。因为 sql 注入的问题,应该尽量避免字符串拼接的方式。

Dapper ORM 使用教程
在 sql 语句中,@后面的 Name 为参数,然后通过匿名类的方式构造参数值。

还有一种只返回一行数据的情况,比如根据 emp_id 来查找,只返回一行,此时使用 QueryFirst() 方法。示例代码如下:

Dapper ORM 使用教程

对数据表的增删改操作,都通过 Execute() 方法,返回值是影响的数据库表的行数。增删改的代码如下:

Dapper ORM 使用教程

我们注意到,在这些方法中,插入和修改都是传入一个 Employee 对象,Dapper 自动在对象中找到相关属性,作为 sql 语句的参数值,这种自动化极大减少了开发人员的手工代码。前提是 entity 类的属性名称与参数名称相同。

以下是上面所有方法的单元测试代码。

Dapper ORM 使用教程

表示层 (Presentation Layer)

本文将使用 WinForm 作界面,打算用两个 Form 来完成对 employee 表的 CRUD。EmpListForm 列示所有的 employees,Form 下面的 BindingNavigator 控件用于导航。

Dapper ORM 使用教程

主要控件的属性设置说明:

bindingNavigator:

  • AddNewItem: 设置为 none,目的是取消控件的默认行为,用自己的代码来代替。
  • DeleteItem: 设置为 none,目的是取消控件的默认行为,用自己的代码来代替
  • Dock: Bottom,显示在表单的下方

bindingSource1:

  • Modifier: 默认为 private,更改为 public。目的是在不用的 Form 中,通过 bindingSource 实现表单记录同步,这是一种常用的方法,请参考本文的参考部分。

DataGridView:

  • Dock: Fill,让 DataGridView 填满表单。

以下是 EmpListForm 的代码:

Dapper ORM 使用教程
EmpListForm 主要负责查看数据和删除数据,新增和修改放在 EmpSingleForm 中。新建一个 EmpSingleForm,设计时界面如下:

Dapper ORM 使用教程
在这个 Form 中,为了能从 EmpListForm 中传入当前的记录,并且标识是修改还是新增,除了默认的构造方法外,另外创建两个构造方法:

Dapper ORM 使用教程
下面是该表单的完整代码:

Dapper ORM 使用教程

IEditableObject 接口

上面的代码已经实现了在界面中对 employees 表数据的增删改查,但有一个问题,在修改数据后,如果不保存,取消修改,EmpSingleForm 关闭后,EmpListForm 还是显示修改后的数据,大家可以自行试试。其原因在与 BindingSource 绑定的数据源,BindingSource 控件提供了诸如 EndEdit() , CancelEdit() 等方法,这些方法会传递到绑定的数据源,但要求数据源(也就是本例的 EmployeeService 类)实现 IEditableObject 接口。该接口的方法提供保存原来数据,CancelEdit() 方法回退到原来状态的机制,EndEdit() 方法提交挂起的修改。以下是IEditableObject 一个简单的实现:

Dapper ORM 使用教程

重构

通过刚才的讲解,不难总结出,Dapper 主要以 Query(), QueryFirst()Execute() 方法来进行增删改查操作,这些方法的代码都用到 IDbConnection 对象以及它的扩展方法。我们可以把这些重复的代码进行抽象,放到专门的 DapperHelper 类中,从而简化 EmployeeService 类的代码编写。在工程中新建一个 DapperHelper 类,对 Query, QueryFirst 和 Execute 方法进行封装:

Dapper ORM 使用教程
然后让 EmployeeService 类继承自 DapperHelper 类,代码大大简化:

Dapper ORM 使用教程

源码

Github - Dapper Demo

参考

相关文章: