Dapper ORM 理解和使用的绝对初学者教程






4.47/5 (9投票s)
本文向完全的初学者介绍 Dapper。
引言
本文向完全的初学者介绍 Dapper
。本文旨在为主要使用 ADO.NET 编写数据访问层的开发人员提供指导。许多经验丰富的开发人员会觉得本文非常基础,但由于本文是从初学者的角度撰写的,我已尽力保持内容简洁。
背景
ADO.NET 是一个非常强大的数据访问框架。ADO.NET 已经存在很多年,有大量系统运行在 ADO.NET 之上。完全不了解 ORM 概念的开发人员可能会问:“Dapper 是什么?使用它有什么好处?它是否可以替代 ADO.NET?”
让我们首先来理解什么是 ORM:ORM(Object-relational mapper)是一种软件,它有助于我们在不兼容的类型系统之间转换数据,即关系数据库和面向对象的编程语言。市面上有许多 ORM 被使用,例如 Entity Framework (EF)、NHibernate、Dapper
等。Dapper
常被称为轻量级 ORM,因为它轻量级,并且不像 NHibernate 或 Entity Framework 那样提供许多功能。Dapper
的主要关注点是性能和易用性。
Entity Framework 等其他 ORM 会根据应用程序中对象的使用/获取方式(CRUD)自动生成查询。这个过程会产生一些性能开销,并且生成的查询对开发人员来说往往是黑盒。而 Dapper
则允许开发人员编写自己的 SQL 来执行数据库操作,并将结果映射到 C# 对象,即 POCO(Plain Old CLR Objects)。由于我们作为开发人员编写查询,而 Dapper 只负责映射到 POCO,因此它被称为微型 ORM 或迷你 ORM。但正因为如此,dapper 的性能几乎与使用纯 ADO.NET 相同。
在本文的其余部分,我们将尝试重点介绍如何使用 Dapper
进行基本的数据库操作。我们将创建一个简单的 API,让用户可以通过 API 本身执行 CRUD 操作,而在内部,我们将使用 dapper 在数据库上执行这些操作。
注意:由于本文的重点是理解 Dapper
,因此在 API 设计中已采取一些简化措施以保持内容简洁。
Dapper 基础知识
让我们从理解 dapper
的基本构造开始,以便能够有效地使用它。
理解 dapper 的最佳方式是将其视为对现有 IDbConnection
对象的扩展(增强功能)。Dapper
实际上就是一组 IDbConnection
的扩展方法。这就是它在性能方面如此强大的原因,但也为用户定义查询留下了空间,这与原生 ADO.NET 类似。所有这些扩展方法都位于“Dapper
”命名空间下,要使用 dapper
方法,我们需要在代码中包含此 namespace
。
现在让我们看看 dapper
中可用于执行 CRUD 操作的基本命令。
Execute
:此扩展方法用于执行INSERT
、UPDATE
和DELETE
查询。当我们想使用dapper
执行存储过程时,也可以使用它。Query
:此扩展方法用于执行SELECT
查询。
除了这两个基本方法之外,还有其他方法,如 QueryFirst
、QueryFirstOrDefault
、QuerySingle
和 QuerySingleOrDefault
。对于使用过 LINQ 的人来说,这些方法不言自明,为了避免初学者跑题,我们就不再讨论它们了。但有一个重要的命令 QueryMultiple
,它可以在一个命令中执行多个查询。为了避免初学者跑题,本文不包含这个命令的介绍,但强烈建议阅读相关资料。
Using the Code
让我们通过创建一个小型示例应用程序来进一步理解这些概念。我们将创建的应用程序是一个简单的图书信息应用程序,它将
- 检索图书列表
- 检索选定图书的详细信息
- 添加新图书
- 删除图书
让我们从查看我们 Books
数据库的数据库架构开始。
以下是创建此 Books
表的脚本
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE TABLE [dbo].[Books](
[ID] [int] IDENTITY(1,1) NOT NULL,
[BookName] [nvarchar](200) NULL,
[ISBN] [nvarchar](50) NULL,
CONSTRAINT [PK_Books] PRIMARY KEY CLUSTERED
(
[ID] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, _
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
现在我们的数据库已准备就绪,我们将把 Dapper
安装到我们的应用程序中。Dapper
可以作为 Nuget 包安装到我们的应用程序中。
在项目中安装 Dapper
后,我们需要做的第一件事是为我们的数据库表创建 POCO。在本例中,我们需要为我们的 Book
对象创建一个类。
public class Book
{
public int ID { get; set; }
public string BookName { get; set;}
public string ISBN { get; set; }
}
现在,由于我们正在处理数据库操作,让我们在数据库调用之上创建一个抽象层,即 repository
类。我们将使用这个 repository
类来执行所有与 Book
相关的数据库操作。让我们从定义 BooksRepository
类的契约开始,并在该契约中定义基本的 CRUD 操作。
interface IRepository<T> where T : class
{
List<T> GetAll();
bool Add(T employee);
T GetById(int id);
bool Update(T employee);
bool Delete(int id);
}
有了这个抽象层,让我们创建一个具体的 BooksRepository
类,其中将包含我们 Book
实体所有数据库 CRUD 操作的逻辑。
using Dapper;
public class BooksRepository : IRepository<Book>
{
private IDbConnection dbConnection = null;
public BooksRepository()
{
dbConnection = new SqlConnection(ConfigReader.ConnectionString);
}
public bool Add(Book book)
{
throw new NotImplementedException();
}
public bool Delete(int id)
{
throw new NotImplementedException();
}
public List<Book> GetAll()
{
throw new NotImplementedException();
}
public Book GetById(int id)
{
throw new NotImplementedException();
}
public bool Update(Book employee)
{
throw new NotImplementedException();
}
}
现在我们已经准备好了用于测试应用程序的所有样板代码。让我们开始看看如何使用 Dapper
进行基本的 CRUD 操作。
Create
由于我们知道 dapper
希望开发人员负责 SQL 查询,所以我们首先定义将用于向 Books
表插入数据的 SQL 查询。
INSERT INTO [dbo].[Books] ([BookName], [ISBN]) VALUES (@bookName ,@isbn)
我们将在应用程序的配置文件中定义此查询(以及所有其他查询),以及其他设置。现在我们已经定义了查询,让我们看看插入数据的代码,我们将用它来将数据插入到 Book
表中。
public bool Add(Book book)
{
var result = false;
try
{
string sql = ConfigReader.InsertCommand;
var count = dbConnection.Execute(sql, book);
result = count > 0;
}
catch { }
return result;
}
读取
既然我们已经看到了插入数据的代码,让我们看看从数据库读取数据的代码。首先,让我们看看如何检索表中的所有项目。让我们从查看此操作的 SQL 查询开始。
SELECT * from Books
现在让我们看看存储库中 GetAll
方法的实现。
public List<Book> GetAll()
{
string sql = ConfigReader.ReadAllCommand;
var queryResult = dbConnection.Query<Book>(sql);
return queryResult.ToList();
}
上面的函数将返回所有图书的列表。现在让我们看看我们将如何检索给定 ID
的单个图书记录。让我们从查看此操作的 SQL 开始。
SELECT * from Books WHERE Id=@Id
现在让我们看看存储库中 GetById
方法的实现。
public Book GetById(int id)
{
Book book = null;
string sql = ConfigReader.ReadOneCommand;
var queryResult = dbConnection.Query<Book>(sql, new { Id = id});
if(queryResult != null)
{
book = queryResult.FirstOrDefault();
}
return book;
}
更新
现在我们完成了读取操作,让我们开始更新操作。让我们看看 update
操作的 SQL 查询。
UPDATE Books SET BookName = @BookName, ISBN = @ISBN WHERE ID = @ID
现在让我们看看存储库中 Update
方法的实现。
public bool Update(Book book)
{
string sql = ConfigReader.UpdateCommand;
var count = dbConnection.Execute(sql, book);
return count > 0;
}
删除
最后,让我们看看如何实现 Delete
操作。
DELETE FROM Books WHERE ID = @Id
现在让我们看看存储库中 Delete
方法的实现。
public bool Delete(int id)
{
string sql = ConfigReader.DeleteCommand;
var count = dbConnection.Execute(sql, new { Id = id });
return count > 0;
}
至此,我们为 Book
实体实现了基本的 CRUD 操作。需要注意的重要一点是,使用 Dapper
与使用 ADO.NET 几乎相同。Dapper
只是负责将从关系型数据获得的结果转换为对象,反之亦然。这正是 dapper
的真正威力所在。如果有人熟悉 ADO.NET,那么使用 dapper
对他们来说简直易如反掌。在下一节中,我们将看看如何使用 dapper
执行存储过程和使用事务。
执行存储过程
使用存储过程就像在 Execute
方法中指定 commandType
一样简单。
string sql = "MyStoredProc";
var result = dbConnection.Execute(sql, commandType: CommandType.StoredProcedure);
使用事务
事务将与 ADO.NET 中的事务相同。我们可以在代码中使用 Transaction
或 TransactionScope
。以下部分显示了一个示例片段。
// Using transactions
using (var tx = connection.BeginTransaction())
{
// All Our Dapper code goes here
tx.Commit();
}
// Using TransactionScope
using (var ts = new TransactionScope())
{
// All Our Dapper code goes here
ts.Complete();
}
至此,我们对如何在应用程序中使用 Dapper 执行基本的 CRUD 操作有了一个大致的了解。在结束本文之前,让我们看看包含所有应用程序设置的配置文件以及我们的 API 控制器代码,可以使用 Postman 等任何 REST 客户端进行测试。参考代码可以在附加的演示项目中找到。
注意:演示项目是一个 ASP.NET Core API 应用程序。
[Route("api/[controller]")]
[ApiController]
public class BookController : ControllerBase
{
IRepository<Book> booksRepository = null;
public BookController()
{
booksRepository = new BooksRepository();
}
[HttpGet]
public IActionResult Get()
{
IList<Book> books = booksRepository.GetAll();
return Ok(books);
}
[HttpGet("{id}", Name = "Get")]
public IActionResult Get(int id)
{
Book book = booksRepository.GetById(id);
if (book != null)
{
return Ok(book);
}
return NotFound();
}
[HttpPost]
public IActionResult Post(Book book)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
if (true == booksRepository.Add(book))
{
return Ok(book);
}
return BadRequest();
}
[HttpPut("{id}")]
public IActionResult Put(int id, Book book)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
book.ID = id;
var result = booksRepository.Update(book);
if (result == true)
{
return Ok(book);
}
return NotFound();
}
[HttpDelete("{id}")]
public IActionResult Delete(int id)
{
if (id <= 0)
{
return BadRequest("invalid id");
}
var result = booksRepository.Delete(id);
if (result == true)
{
return Ok();
}
return NotFound();
}
}
关注点
在本文中,我们研究了如何在 .NET 应用程序中使用 Dapper
执行基本的 CRUD 操作。本文从初学者的角度撰写。希望这篇文章有所启发。
历史
- 2018 年 8 月 3 日:第一版