Dapper和DapperExtensions都是.NET平台上的数据访问库,它们各自具有特点和优势,适用于不同的开发场景。
- Dapper 适合那些需要高性能、低开销,并且愿意编写SQL语句的开发者。它提供了极大的灵活性和控制力,适用于对性能要求极高的场景。
- DapperExtensions 则适合那些希望简化CRUD操作,减少SQL编写,同时保持高性能的开发者。它提供了更高级的查询功能和零配置的便利性,适用于快速开发和中低复杂度的项目。
1. 简介
1.1 Dapper简介
Dapper 是一个轻量级的 ORM(对象关系映射)工具,它基于 ADO.NET 实现,能够显著提高数据库操作的性能和效率。Dapper 由 Stack Overflow 团队开发,最初是为了优化其网站的数据库访问性能而设计的。它通过扩展 IDbConnection
接口,提供了一种简单而高效的方式来执行 SQL 查询和更新操作。
-
性能优势:Dapper 的性能非常出色,因为它直接操作
IDbConnection
,避免了传统 ORM 框架中复杂的映射和缓存机制。在许多场景下,Dapper 的性能比传统 ORM 框架高出数倍。 -
易用性:尽管 Dapper 是一个轻量级的工具,但它提供了非常简洁的 API,使得开发者可以轻松地将 SQL 查询结果映射到对象中。例如,使用 Dapper 查询数据的代码如下:
-
var connection = new SqlConnection(connectionString); var result = connection.Query<User>("SELECT * FROM Users WHERE Id = @Id", new { Id = userId });
-
灵活性:Dapper 允许开发者直接编写 SQL 语句,而不是依赖于复杂的 LINQ 查询。这使得开发者可以更灵活地控制数据库操作,同时也能更好地优化查询性能。
1.2 DapperExtensions简介
DapperExtensions 是一个基于 Dapper 的扩展库,它提供了更高级的 ORM 功能,使得开发者可以更方便地进行数据库操作。DapperExtensions 提供了诸如增删改查、分页查询、条件查询等功能,大大简化了开发工作。
-
功能丰富:DapperExtensions 提供了丰富的 API,支持常见的数据库操作。例如,使用 DapperExtensions 插入数据的代码如下:
-
var connection = new SqlConnection(connectionString); var user = new User { Name = "John", Age = 30 }; connection.Insert(user);
-
自动映射:DapperExtensions 能够自动将对象属性映射到数据库表的字段,开发者无需手动编写映射代码。这使得开发更加高效,同时也减少了出错的可能性。
-
条件查询:DapperExtensions 提供了强大的条件查询功能,支持多种条件组合。例如,使用 DapperExtensions 进行条件查询的代码如下:
-
var connection = new SqlConnection(connectionString); var predicate = Predicates.Field<User>(f => f.Age, Operator.Gt, 25); var users = connection.GetList<User>(predicate);
-
分页查询:DapperExtensions 提供了分页查询功能,使得开发者可以轻松地实现分页功能。例如,使用 DapperExtensions 进行分页查询的代码如下:
-
var connection = new SqlConnection(connectionString); var predicate = Predicates.Field<User>(f => f.Age, Operator.Gt, 25); var pagedUsers = connection.GetPage<User>(predicate, null, 1, 10);
2. 环境搭建
2.1 安装 Dapper
要开始使用 Dapper,首先需要在项目中安装 Dapper 包。可以通过 NuGet 包管理器来安装。在 Visual Studio 中,右键点击项目,选择“管理 NuGet 包”,然后搜索并安装 Dapper
包。
安装命令(适用于 .NET Core 和 .NET Framework 项目):
dotnet add package Dapper
或者在 NuGet 包管理器控制台中运行:
Install-Package Dapper
安装完成后,你可以在项目中引入 Dapper 命名空间:
using Dapper;
2.2 安装 DapperExtensions
接下来,安装 DapperExtensions。DapperExtensions 是一个基于 Dapper 的扩展库,提供了更高级的 ORM 功能。同样可以通过 NuGet 包管理器来安装。
安装命令(适用于 .NET Core 和 .NET Framework 项目):
dotnet add package DapperExtensions
或者在 NuGet 包管理器控制台中运行:
Install-Package DapperExtensions
安装完成后,你可以在项目中引入 DapperExtensions 命名空间:
using DapperExtensions;
配置数据库连接
在使用 Dapper 和 DapperExtensions 之前,需要配置数据库连接字符串。以下是一个简单的示例,展示如何在 appsettings.json
文件中配置连接字符串(适用于 .NET Core 项目):
{
"ConnectionStrings": {
"DefaultConnection": "Server=your_server;Database=your_database;User Id=your_user;Password=your_password;"
}
}
在代码中,可以通过 Configuration
对象读取连接字符串:
using Microsoft.Extensions.Configuration;
public class Program
{
public static void Main(string[] args)
{
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build();
string connectionString = configuration.GetConnectionString("DefaultConnection");
using (var connection = new SqlConnection(connectionString))
{
// 在这里使用 Dapper 和 DapperExtensions
}
}
}
通过以上步骤,你已经成功搭建了 Dapper 和 DapperExtensions 的开发环境,可以开始使用它们进行数据库操作了。
3. Dapper基本使用
3.1 创建数据库连接
在使用 Dapper 进行数据库操作之前,需要先创建一个数据库连接。Dapper 基于 ADO.NET 的 IDbConnection
接口,因此可以使用任何实现了该接口的数据库连接对象。以下是一个使用 SqlConnection
创建数据库连接的示例:
using System.Data.SqlClient;
string connectionString = "Server=your_server;Database=your_database;User Id=your_user;Password=your_password;";
using (var connection = new SqlConnection(connectionString))
{
// 在这里使用 Dapper 进行数据库操作
}
在实际项目中,通常会将连接字符串配置在配置文件中,然后通过配置管理工具读取。例如,在 .NET Core 项目中,可以在 appsettings.json
文件中配置连接字符串,然后通过 Configuration
对象读取:
using Microsoft.Extensions.Configuration;
var configuration = new ConfigurationBuilder()
.AddJsonFile("appsettings.json")
.Build();
string connectionString = configuration.GetConnectionString("DefaultConnection");
using (var connection = new SqlConnection(connectionString))
{
// 在这里使用 Dapper 进行数据库操作
}
创建数据库连接后,就可以使用 Dapper 提供的方法进行数据库操作了。
3.2 执行 CRUD 操作
Dapper 提供了简单易用的 API 来执行数据库的增删改查(CRUD)操作。以下是一些常见的 CRUD 操作示例:
查询操作
Dapper 提供了 Query<T>
方法,用于执行 SQL 查询并将结果映射到对象中。以下是一个查询单个对象的示例:
using System.Data.SqlClient;
using Dapper;
string connectionString = "Server=your_server;Database=your_database;User Id=your_user;Password=your_password;";
using (var connection = new SqlConnection(connectionString))
{
var user = connection.QuerySingleOrDefault<User>("SELECT * FROM Users WHERE Id = @Id", new { Id = 1 });
Console.WriteLine(user.Name);
}
在上面的代码中,QuerySingleOrDefault<T>
方法用于查询单个对象。如果查询结果为空,则返回 default(T)
。
Dapper 还提供了 Query<T>
方法,用于查询多个对象。以下是一个查询多个对象的示例:
using System.Data.SqlClient;
using Dapper;
string connectionString = "Server=your_server;Database=your_database;User Id=your_user;Password=your_password;";
using (var connection = new SqlConnection(connectionString))
{
var users = connection.Query<User>("SELECT * FROM Users WHERE Age > @Age", new { Age = 25 });
foreach (var user in users)
{
Console.WriteLine(user.Name);
}
}
在上面的代码中,Query<T>
方法用于查询多个对象,并将结果映射到 IEnumerable<T>
中。
插入操作
Dapper 提供了 Execute
方法,用于执行 SQL 插入操作。以下是一个插入单个对象的示例:
using System.Data.SqlClient;
using Dapper;
string connectionString = "Server=your_server;Database=your_database;User Id=your_user;Password=your_password;";
using (var connection = new SqlConnection(connectionString))
{
var user = new User { Name = "John", Age = 30 };
connection.Execute("INSERT INTO Users (Name, Age) VALUES (@Name, @Age)", user);
}
在上面的代码中,Execute
方法用于执行 SQL 插入操作,并将对象的属性作为参数传递给 SQL 语句。
更新操作
Dapper 提供了 Execute
方法,用于执行 SQL 更新操作。以下是一个更新单个对象的示例:
using System.Data.SqlClient;
using Dapper;
string connectionString = "Server=your_server;Database=your_database;User Id=your_user;Password=your_password;";
using (var connection = new SqlConnection(connectionString))
{
var user = new User { Id = 1, Name = "John", Age = 31 };
connection.Execute("UPDATE Users SET Name = @Name, Age = @Age WHERE Id = @Id", user);
}
在上面的代码中,Execute
方法用于执行 SQL 更新操作,并将对象的属性作为参数传递给 SQL 语句。
删除操作
Dapper 提供了 Execute
方法,用于执行 SQL 删除操作。以下是一个删除单个对象的示例:
using System.Data.SqlClient;
using Dapper;
string connectionString = "Server=your_server;Database=your_database;User Id=your_user;Password=your_password;";
using (var connection = new SqlConnection(connectionString))
{
connection.Execute("DELETE FROM Users WHERE Id = @Id", new { Id = 1 });
}
在上面的代码中,Execute
方法用于执行 SQL 删除操作,并将对象的属性作为参数传递给 SQL 语句。
通过以上示例,可以看到 Dapper 提供了简单易用的 API 来执行数据库的 CRUD 操作。开发者可以直接编写 SQL 语句,而无需依赖于复杂的 ORM 框架。这使得开发者可以更灵活地控制数据库操作,同时也能更好地优化查询性能。
4. DapperExtensions使用
4.1 实体类映射
DapperExtensions通过自动映射和自定义映射的方式,将实体类与数据库表进行映射,使得开发者无需手动编写大量的映射代码,从而提高开发效率。
自动映射
DapperExtensions的自动映射功能基于一些默认的命名约定。例如,它假设实体类的名称与数据库表的名称一致,实体类的属性名称与数据库表的字段名称一致。如果实体类的主键属性名为Id
,DapperExtensions会自动将其识别为主键。此外,如果主键类型为int
或long
,DapperExtensions会将其视为自增主键;如果主键类型为Guid
,DapperExtensions会自动为其生成一个新的Guid
值。
例如,以下是一个简单的实体类User
:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
假设数据库中有一个名为Users
的表,其结构如下:
CREATE TABLE Users (
Id INT PRIMARY KEY IDENTITY(1,1),
Name NVARCHAR(50),
Age INT
);
在这种情况下,DapperExtensions可以自动将User
类与Users
表进行映射,无需任何额外的配置。
自定义映射
虽然自动映射可以满足大多数场景的需求,但在某些情况下,开发者可能需要对映射关系进行自定义。DapperExtensions提供了ClassMapper<T>
类,允许开发者自定义实体类与数据库表之间的映射关系。
以下是一个自定义映射的示例:
public class UserMap : ClassMapper<User>
{
public UserMap()
{
Table("Users"); // 指定数据库表名
Map(x => x.Id).Key(KeyType.Identity); // 指定主键及其类型
Map(x => x.Name).Column("UserName"); // 指定字段映射
Map(x => x.Age).Ignore(); // 忽略某个字段
}
}
在使用自定义映射时,需要在程序启动时注册映射关系:
DapperExtensions.DapperExtensions.DefaultMapper = typeof(UserMap);
通过自定义映射,开发者可以灵活地控制实体类与数据库表之间的映射关系,满足复杂的业务需求。
4.2 封装的CRUD操作
DapperExtensions封装了大量的CRUD操作函数,使得开发者可以更方便地进行数据库操作,无需手动编写SQL语句。
插入操作
DapperExtensions提供了Insert
方法,用于插入数据。以下是一个插入单个对象的示例:
var user = new User { Name = "John", Age = 30 };
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
int id = connection.Insert(user);
Console.WriteLine($"Inserted user with ID: {id}");
}
在上面的代码中,Insert
方法会自动将对象的属性映射到数据库表的字段,并生成相应的SQL插入语句。如果主键是自增的,Insert
方法会返回新插入的主键值。
查询操作
DapperExtensions提供了多种查询方法,包括Get
、GetList
和GetPage
等。以下是一些查询操作的示例:
查询单个对象
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
var user = connection.Get<User>(1); // 根据主键查询单个对象
Console.WriteLine(user.Name);
}
查询多个对象
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
var predicate = Predicates.Field<User>(f => f.Age, Operator.Gt, 25); // 创建条件
var users = connection.GetList<User>(predicate); // 查询满足条件的多个对象
foreach (var user in users)
{
Console.WriteLine(user.Name);
}
}
分页查询
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
var predicate = Predicates.Field<User>(f => f.Age, Operator.Gt, 25); // 创建条件
var pagedUsers = connection.GetPage<User>(predicate, null, 1, 10); // 分页查询
foreach (var user in pagedUsers)
{
Console.WriteLine(user.Name);
}
}
在分页查询中,GetPage
方法的参数包括查询条件、排序字段、当前页码和每页显示的记录数。DapperExtensions会自动根据这些参数生成分页查询的SQL语句。
更新操作
DapperExtensions提供了Update
方法,用于更新数据。以下是一个更新单个对象的示例:
var user = new User { Id = 1, Name = "John", Age = 31 };
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
connection.Update(user); // 更新对象
}
在上面的代码中,Update
方法会自动将对象的属性映射到数据库表的字段,并生成相应的SQL更新语句。只有对象的非主键属性会被更新。
删除操作
DapperExtensions提供了Delete
方法,用于删除数据。以下是一个删除单个对象的示例:
var user = new User { Id = 1 };
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
connection.Delete(user); // 删除对象
}
在上面的代码中,Delete
方法会根据对象的主键生成相应的SQL删除语句。
通过封装的CRUD操作函数,DapperExtensions大大简化了数据库操作的代码,使得开发者可以更专注于业务逻辑的实现,而无需花费大量时间编写SQL语句。
5. 高级功能
5.1 谓词系统
DapperExtensions 的谓词系统是其强大的功能之一,它允许开发者以编程的方式构建复杂的查询条件,而无需手动拼接 SQL 语句。这不仅提高了代码的可读性和可维护性,还减少了 SQL 注入的风险。
谓词的创建
谓词是 DapperExtensions 中用于定义查询条件的对象。可以通过 Predicates
类来创建各种类型的谓词。以下是一些常见的谓词创建示例:
单字段条件
var predicate = Predicates.Field<User>(f => f.Age, Operator.Gt, 25);
上述代码创建了一个条件,表示查询 Age
大于 25 的用户。
多字段组合条件
var predicate = Predicates.Field<User>(f => f.Age, Operator.Gt, 25)
.And<User>(f => f.Name, Operator.Eq, "John");
上述代码创建了一个组合条件,表示查询 Age
大于 25 且 Name
等于 "John" 的用户。
使用逻辑运算符
var predicate = Predicates.Field<User>(f => f.Age, Operator.Gt, 25)
.Or<User>(f => f.Name, Operator.Eq, "John");
上述代码创建了一个条件,表示查询 Age
大于 25 或 Name
等于 "John" 的用户。
谓词的使用
创建好谓词后,可以将其用于查询操作。以下是一些使用谓词进行查询的示例:
查询满足条件的单个对象
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
var user = connection.Get<User>(predicate);
Console.WriteLine(user.Name);
}
查询满足条件的多个对象
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
var users = connection.GetList<User>(predicate);
foreach (var user in users)
{
Console.WriteLine(user.Name);
}
}
分页查询
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
var pagedUsers = connection.GetPage<User>(predicate, null, 1, 10);
foreach (var user in pagedUsers)
{
Console.WriteLine(user.Name);
}
}
通过谓词系统,DapperExtensions 提供了一种灵活且强大的方式来构建查询条件,使得开发者可以更方便地进行复杂的查询操作。
5.2 分页查询
分页查询是许多应用程序中常见的需求,DapperExtensions 提供了简单而高效的分页查询功能。通过分页查询,可以将查询结果分成多个页面,每次只返回指定页面的数据,从而提高性能和用户体验。
分页查询的实现
DapperExtensions 的分页查询功能通过 GetPage
方法实现。该方法需要以下参数:
-
查询条件(谓词)
-
排序字段
-
当前页码
-
每页显示的记录数
以下是一个分页查询的示例:
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
var predicate = Predicates.Field<User>(f => f.Age, Operator.Gt, 25); // 创建查询条件
var pagedUsers = connection.GetPage<User>(predicate, null, 1, 10); // 获取第1页,每页10条记录
foreach (var user in pagedUsers)
{
Console.WriteLine(user.Name);
}
}
在上述代码中:
-
predicate
是查询条件,表示查询Age
大于 25 的用户。 -
null
表示不指定排序字段,DapperExtensions 会默认按照主键字段排序。 -
1
表示当前页码为第 1 页。 -
10
表示每页显示 10 条记录。
分页查询的结果
GetPage
方法返回的是一个分页查询的结果对象,其中包含了以下信息:
-
当前页的数据
-
总记录数
-
总页数
可以通过以下方式获取这些信息:
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
var predicate = Predicates.Field<User>(f => f.Age, Operator.Gt, 25);
var pagedUsers = connection.GetPage<User>(predicate, null, 1, 10);
// 获取当前页的数据
foreach (var user in pagedUsers)
{
Console.WriteLine(user.Name);
}
// 获取总记录数
Console.WriteLine($"Total records: {pagedUsers.TotalCount}");
// 获取总页数
Console.WriteLine($"Total pages: {pagedUsers.TotalPages}");
}
分页查询的性能优化
DapperExtensions 的分页查询功能在底层会生成高效的 SQL 分页查询语句,避免了全表扫描带来的性能问题。例如,对于 SQL Server 数据库,DapperExtensions 会使用 OFFSET
和 FETCH
子句来实现分页查询,从而提高查询效率。
通过 DapperExtensions 的分页查询功能,开发者可以轻松实现分页查询的需求,同时保证查询的性能和效率。
6. 性能优化
6.1 缓存机制
Dapper 和 DapperExtensions 在性能优化方面提供了多种机制,其中缓存机制是关键之一。通过合理使用缓存,可以显著减少数据库的访问次数,从而提高应用程序的整体性能。
查询结果缓存
Dapper 本身不直接提供缓存功能,但可以通过与外部缓存工具(如 MemoryCache 或 Redis)结合来实现查询结果的缓存。以下是一个使用 MemoryCache 缓存查询结果的示例:
using System.Runtime.Caching;
public class UserRepository
{
private readonly string _connectionString;
private readonly MemoryCache _cache = MemoryCache.Default;
public UserRepository(string connectionString)
{
_connectionString = connectionString;
}
public User GetUserById(int id)
{
string cacheKey = $"User_{id}";
if (_cache.Contains(cacheKey))
{
return (User)_cache[cacheKey];
}
using (var connection = new SqlConnection(_connectionString))
{
var user = connection.QuerySingleOrDefault<User>("SELECT * FROM Users WHERE Id = @Id", new { Id = id });
if (user != null)
{
_cache.Set(cacheKey, user, new CacheItemPolicy { AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(5) });
}
return user;
}
}
}
在上述代码中,查询结果被存储在 MemoryCache
中,缓存的有效期为 5 分钟。如果缓存中存在数据,则直接从缓存中读取,避免了对数据库的重复查询。
缓存策略
缓存策略的选择对性能优化至关重要。常见的缓存策略包括:
-
时间过期缓存:设置缓存的有效期,如上例中的 5 分钟。适用于不频繁更新的数据。
-
基于版本的缓存:为缓存数据添加版本号,当数据更新时,更新版本号并刷新缓存。
-
分布式缓存:使用 Redis 等分布式缓存系统,适用于分布式应用程序和高并发场景。
缓存的优缺点
-
优点:
-
减少数据库访问次数:显著降低数据库的压力,提高查询效率。
-
提高响应速度:从缓存中读取数据比从数据库中读取更快,提升用户体验。
-
-
缺点:
-
数据一致性问题:缓存数据可能与数据库中的数据不一致,需要合理设计缓存失效策略。
-
增加复杂性:引入缓存机制会增加系统的复杂性,需要维护缓存与数据库之间的同步。
-
6.2 批量操作
批量操作是提高数据库性能的另一种有效手段。Dapper 和 DapperExtensions 提供了支持批量操作的功能,可以显著减少数据库的交互次数,从而提高性能。
批量插入
DapperExtensions 提供了 Insert
方法,可以用于批量插入数据。以下是一个批量插入的示例:
using System.Collections.Generic;
public void InsertUsers(List<User> users)
{
using (var connection = new SqlConnection(_connectionString))
{
connection.Open();
foreach (var user in users)
{
connection.Insert(user);
}
}
}
在上述代码中,通过循环调用 Insert
方法,可以将多个用户对象批量插入到数据库中。虽然这种方式简单,但在处理大量数据时,可能会导致性能问题。为了进一步优化,可以使用 SqlTransaction
来确保批量操作的原子性:
using (var connection = new SqlConnection(_connectionString))
{
connection.Open();
using (var transaction = connection.BeginTransaction())
{
foreach (var user in users)
{
connection.Insert(user, transaction);
}
transaction.Commit();
}
}
批量更新
DapperExtensions 的 Update
方法也可以用于批量更新数据。以下是一个批量更新的示例:
public void UpdateUsers(List<User> users)
{
using (var connection = new SqlConnection(_connectionString))
{
connection.Open();
foreach (var user in users)
{
connection.Update(user);
}
}
}
与批量插入类似,批量更新也可以通过 SqlTransaction
来确保操作的原子性:
using (var connection = new SqlConnection(_connectionString))
{
connection.Open();
using (var transaction = connection.BeginTransaction())
{
foreach (var user in users)
{
connection.Update(user, transaction);
}
transaction.Commit();
}
}
批量删除
DapperExtensions 的 Delete
方法同样支持批量删除操作。以下是一个批量删除的示例:
public void DeleteUsers(List<int> userIds)
{
using (var connection = new SqlConnection(_connectionString))
{
connection.Open();
foreach (var userId in userIds)
{
var user = new User { Id = userId };
connection.Delete(user);
}
}
}
为了提高性能,可以使用 SqlTransaction
来确保批量删除操作的原子性:
using (var connection = new SqlConnection(_connectionString))
{
connection.Open();
using (var transaction = connection.BeginTransaction())
{
foreach (var userId in userIds)
{
var user = new User { Id = userId };
connection.Delete(user, transaction);
}
transaction.Commit();
}
}
批量操作的性能优势
-
减少数据库交互次数:批量操作可以将多个数据库操作合并为一次交互,显著减少网络延迟和数据库连接的开销。
-
提高事务效率:通过
SqlTransaction
确保批量操作的原子性,可以避免部分操作失败导致的数据不一致问题。 -
优化资源利用:批量操作可以更好地利用数据库的资源,提高数据库的吞吐量。
批量操作的注意事项
-
事务管理:确保批量操作的原子性,使用
SqlTransaction
来管理事务。 -
错误处理:在批量操作中,需要合理处理可能出现的错误,避免因部分操作失败导致整个事务回滚。
-
性能测试:在实际应用中,需要对批量操作进行性能测试,以确保其在高并发场景下的稳定性和效率。
通过合理使用缓存机制和批量操作,可以显著提升 Dapper 和 DapperExtensions 在数据库操作中的性能表现,从而提高应用程序的整体性能和用户体验。
7. 最佳实践
7.1 代码示例
以下是结合 Dapper 和 DapperExtensions 的最佳实践代码示例,展示如何高效地使用它们进行数据库操作。
数据库连接管理
在实际项目中,通常会使用依赖注入来管理数据库连接。以下是一个使用依赖注入的示例:
using Microsoft.Extensions.DependencyInjection;
using System.Data.SqlClient;
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
string connectionString = "Server=your_server;Database=your_database;User Id=your_user;Password=your_password;";
services.AddTransient<IDbConnection>(provider => new SqlConnection(connectionString));
}
}
public class UserRepository
{
private readonly IDbConnection _connection;
public UserRepository(IDbConnection connection)
{
_connection = connection;
}
public User GetUserById(int id)
{
return _connection.QuerySingleOrDefault<User>("SELECT * FROM Users WHERE Id = @Id", new { Id = id });
}
}
使用 DapperExtensions 的自动映射
自动映射可以大大简化开发工作。以下是一个自动映射的示例:
public class User
{
public int Id { get; set; }
public string Name { get; set; }
public int Age { get; set; }
}
public class UserRepository
{
private readonly IDbConnection _connection;
public UserRepository(IDbConnection connection)
{
_connection = connection;
}
public User GetUserById(int id)
{
return _connection.Get<User>(id);
}
public void InsertUser(User user)
{
_connection.Insert(user);
}
public void UpdateUser(User user)
{
_connection.Update(user);
}
public void DeleteUser(User user)
{
_connection.Delete(user);
}
}
使用 Dapper 的复杂查询
对于复杂的查询,Dapper 提供了更高的灵活性。以下是一个复杂的查询示例:
public class UserRepository
{
private readonly IDbConnection _connection;
public UserRepository(IDbConnection connection)
{
_connection = connection;
}
public List<User> GetUsersWithComplexQuery(int age, string name)
{
var query = @"
SELECT *
FROM Users
WHERE Age > @Age AND Name LIKE @Name
ORDER BY Age DESC";
return _connection.Query<User>(query, new { Age = age, Name = $"%{name}%" }).ToList();
}
}
使用 DapperExtensions 的分页查询
分页查询是常见的需求,DapperExtensions 提供了简单高效的分页功能。以下是一个分页查询的示例:
public class UserRepository
{
private readonly IDbConnection _connection;
public UserRepository(IDbConnection connection)
{
_connection = connection;
}
public PagedList<User> GetUsersWithPagination(int page, int pageSize)
{
var predicate = Predicates.Field<User>(f => f.Age, Operator.Gt, 25);
var pagedUsers = _connection.GetPage<User>(predicate, null, page, pageSize);
return new PagedList<User>
{
Data = pagedUsers,
TotalCount = pagedUsers.TotalCount,
TotalPages = pagedUsers.TotalPages
};
}
}
public class PagedList<T>
{
public IEnumerable<T> Data { get; set; }
public int TotalCount { get; set; }
public int TotalPages { get; set; }
}
7.2 常见问题
以下是使用 Dapper 和 DapperExtensions 时常见的问题及解决方案。
1. 如何处理数据库连接的打开和关闭?
在使用 Dapper 和 DapperExtensions 时,建议使用 using
语句来管理数据库连接,确保连接在使用后能够正确关闭。例如:
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
var user = connection.Get<User>(1);
}
如果使用依赖注入,可以在构造函数中注入 IDbConnection
,并在需要时打开连接。例如:
public class UserRepository
{
private readonly IDbConnection _connection;
public UserRepository(IDbConnection connection)
{
_connection = connection;
}
public User GetUserById(int id)
{
_connection.Open();
var user = _connection.Get<User>(id);
_connection.Close();
return user;
}
}
2. 如何处理事务?
Dapper 和 DapperExtensions 支持事务操作,可以通过 IDbTransaction
来管理事务。以下是一个事务操作的示例:
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
using (var transaction = connection.BeginTransaction())
{
try
{
var user = new User { Name = "John", Age = 30 };
connection.Insert(user, transaction);
connection.Update(user, transaction);
transaction.Commit();
}
catch (Exception)
{
transaction.Rollback();
throw;
}
}
}
3. 如何处理 SQL 注入问题?
Dapper 和 DapperExtensions 都支持参数化查询,这可以有效防止 SQL 注入问题。例如:
var query = "SELECT * FROM Users WHERE Name = @Name";
var users = connection.Query<User>(query, new { Name = "John" });
在上述代码中,@Name
是一个参数,Dapper 会自动处理参数的值,避免 SQL 注入问题。
4. 如何处理复杂的查询条件?
对于复杂的查询条件,可以使用 DapperExtensions 的谓词系统来构建条件。例如:
var predicate = Predicates.Field<User>(f => f.Age, Operator.Gt, 25)
.And<User>(f => f.Name, Operator.Eq, "John");
var users = connection.GetList<User>(predicate);
5. 如何处理分页查询的性能问题?
DapperExtensions 的分页查询功能在底层会生成高效的 SQL 分页查询语句,但为了进一步优化性能,可以结合缓存机制。例如:
using System.Runtime.Caching;
public class UserRepository
{
private readonly IDbConnection _connection;
private readonly MemoryCache _cache = MemoryCache.Default;
public UserRepository(IDbConnection connection)
{
_connection = connection;
}
public PagedList<User> GetUsersWithPagination(int page, int pageSize)
{
string cacheKey = $"Users_Page_{page}_PageSize_{pageSize}";
if (_cache.Contains(cacheKey))
{
return (PagedList<User>)_cache[cacheKey];
}
var predicate = Predicates.Field<User>(f => f.Age, Operator.Gt, 25);
var pagedUsers = _connection.GetPage<User>(predicate, null, page, pageSize);
_cache.Set(cacheKey, pagedUsers, new CacheItemPolicy { AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(5) });
return pagedUsers;
}
}
通过合理使用缓存机制,可以显著减少数据库的访问次数,从而提高分页查询的性能。
6. 如何处理批量操作的性能问题?
Dapper 和 DapperExtensions 支持批量操作,可以通过 SqlTransaction
来确保批量操作的原子性。以下是一个批量插入的示例:
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
using (var transaction = connection.BeginTransaction())
{
foreach (var user in users)
{
connection.Insert(user, transaction);
}
transaction.Commit();
}
}
通过使用事务,可以确保批量操作的原子性,同时减少数据库的交互次数,从而提高性能。
7. 如何处理跨数据库操作?
Dapper 和 DapperExtensions 支持多种数据库,包括 SQL Server、MySQL、PostgreSQL 等。可以通过配置不同的数据库连接字符串来支持跨数据库操作。例如:
string sqlConnectionString = "Server=your_server;Database=your_database;User Id=your_user;Password=your_password;";
string mysqlConnectionString = "Server=your_mysql_server;Database=your_database;User Id=your_user;Password=your_password;";
using (var sqlConnection = new SqlConnection(sqlConnectionString))
{
var users = sqlConnection.Query<User>("SELECT * FROM Users");
}
using (var mysqlConnection = new MySqlConnection(mysqlConnectionString))
{
var users = mysqlConnection.Query<User>("SELECT * FROM Users");
}
通过合理配置连接字符串,可以轻松实现跨数据库操作。
8. 如何处理异步操作?
Dapper 和 DapperExtensions 都支持异步操作,可以通过 async
和 await
关键字来实现异步数据库操作。以下是一个异步查询的示例:
public async Task<User> GetUserByIdAsync(int id)
{
using (var connection = new SqlConnection(connectionString))
{
await connection.OpenAsync();
return await connection.QuerySingleOrDefaultAsync<User>("SELECT * FROM Users WHERE Id = @Id", new { Id = id });
}
}
通过使用异步操作,可以提高应用程序的响应速度,尤其是在高并发场景下。
8. 总结
Dapper 是由 Stack Overflow 团队开发的一个简单对象映射器,它扩展了 IDbConnection 接口,使得执行 SQL 查询并将其结果映射到 C# 对象变得非常容易。Dapper 主要有以下特点:
- 性能:Dapper 被设计为高性能的 ORM,它的速度几乎接近于使用 ADO.NET 手动编写数据访问代码。
- 灵活性:支持存储过程调用、动态查询构建以及多种数据类型的映射。
- 简单性:API 设计简洁,易于学习和使用。
DapperExtensions
DapperExtensions 是在 Dapper 基础之上构建的一个库,目的是减少手写 SQL 语句的需求。它提供了更高级别的抽象,可以自动生成一些基本的 CRUD 操作(创建、读取、更新、删除),从而减少了开发者的工作量。主要特性包括:
- 自动映射:基于约定和配置,能够自动将表与实体类进行映射。
- CRUD 方法:提供了一套通用的 CRUD 方法,无需手动编写这些操作的 SQL 语句。
- 可扩展性:允许通过自定义约定或配置来调整其行为,以满足特定需求。
如果你想要对数据库操作有完全的控制,并且不介意编写 SQL 语句,那么 Dapper 是一个非常好的选择。而如果你希望最小化手写的 SQL 代码量,并且愿意接受一些默认的行为和限制,DapperExtensions 可能更适合你。两者都是轻量级的解决方案,不会像一些大型 ORM(如 Entity Framework)那样引入过多的复杂性和开销。