65.9K
CodeProject 正在变化。 阅读更多。
Home

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

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.47/5 (9投票s)

2018 年 8 月 3 日

CPOL

7分钟阅读

viewsIcon

34872

downloadIcon

568

本文向完全的初学者介绍 Dapper。

引言

本文向完全的初学者介绍 Dapper。本文旨在为主要使用 ADO.NET 编写数据访问层的开发人员提供指导。许多经验丰富的开发人员会觉得本文非常基础,但由于本文是从初学者的角度撰写的,我已尽力保持内容简洁。

背景

ADO.NET 是一个非常强大的数据访问框架。ADO.NET 已经存在很多年,有大量系统运行在 ADO.NET 之上。完全不了解 ORM 概念的开发人员可能会问:“Dapper 是什么?使用它有什么好处?它是否可以替代 ADO.NET?”

让我们首先来理解什么是 ORM:ORM(Object-relational mapper)是一种软件,它有助于我们在不兼容的类型系统之间转换数据,即关系数据库和面向对象的编程语言。市面上有许多 ORM 被使用,例如 Entity Framework (EF)NHibernateDapper 等。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:此扩展方法用于执行 INSERTUPDATEDELETE 查询。当我们想使用 dapper 执行存储过程时,也可以使用它。
  • Query:此扩展方法用于执行 SELECT 查询。

除了这两个基本方法之外,还有其他方法,如 QueryFirstQueryFirstOrDefaultQuerySingleQuerySingleOrDefault。对于使用过 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 中的事务相同。我们可以在代码中使用 TransactionTransactionScope。以下部分显示了一个示例片段。

// 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 日:第一版
© . All rights reserved.