最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C#常用類(lèi)庫(kù)Dapper詳解

 更新時(shí)間:2026年03月12日 09:50:57   作者:阿蒙Armon  
Dapper是一款輕量級(jí)對(duì)象關(guān)系映射(ORM)工具,核心目標(biāo)是“簡(jiǎn)化數(shù)據(jù)映射,不犧牲性能與靈活性”,本文給大家介紹C#常用類(lèi)庫(kù)Dapper的相關(guān)知識(shí),感興趣的朋友跟隨小編一起看看吧

作為C#開(kāi)發(fā)者,在數(shù)據(jù)訪問(wèn)層(DAL)的開(kāi)發(fā)中,我們始終面臨一個(gè)選擇:是使用EF Core這類(lèi)“大而全”的ORM框架,還是選擇輕量級(jí)、高性能的工具?對(duì)于追求極致性能、需要靈活控制SQL、厭惡“黑箱操作”的場(chǎng)景,Dapper無(wú)疑是最優(yōu)解。

Dapper是由Stack Overflow團(tuán)隊(duì)開(kāi)發(fā)的輕量級(jí)ORM工具,它并非替代EF Core,而是以“簡(jiǎn)潔、高效、靈活”為核心,在保持原生SQL控制力的同時(shí),簡(jiǎn)化數(shù)據(jù)映射的繁瑣操作,完美平衡了“性能”與“開(kāi)發(fā)效率”。本文將從基礎(chǔ)用法→核心特性→高級(jí)技巧→性能優(yōu)化→避坑指南,全方位、有深度地解析Dapper,結(jié)合實(shí)際項(xiàng)目場(chǎng)景,幫你徹底掌握這款“性能王者”級(jí)別的數(shù)據(jù)訪問(wèn)工具。

一、前言:Dapper的定位與核心優(yōu)勢(shì)

在正式講解用法前,我們先明確Dapper的核心定位、優(yōu)勢(shì),以及它與EF Core的差異——這是實(shí)際項(xiàng)目中框架選型的關(guān)鍵,也是理解Dapper設(shè)計(jì)理念的基礎(chǔ)。

1. 核心定位

Dapper是一款輕量級(jí)對(duì)象關(guān)系映射(ORM)工具,核心目標(biāo)是“簡(jiǎn)化數(shù)據(jù)映射,不犧牲性能與靈活性”。它本質(zhì)上是對(duì)ADO.NET的封裝,屏蔽了ADO.NET中繁瑣的連接、命令、數(shù)據(jù)讀取操作,同時(shí)保留了原生SQL的全部控制力,避免了重量級(jí)ORM(如EF Core)的性能開(kāi)銷(xiāo)和“黑箱”問(wèn)題。

Dapper支持所有主流關(guān)系型數(shù)據(jù)庫(kù)(SQL Server、MySQL、Oracle、PostgreSQL等),兼容.NET Framework 4.5+、.NET Core 2.0+、.NET 5/6/7/8等所有主流.NET平臺(tái),是分層架構(gòu)、微服務(wù)、高性能系統(tǒng)中數(shù)據(jù)訪問(wèn)層的首選工具。

2. 核心優(yōu)勢(shì)(對(duì)比EF Core)

Dapper的核心競(jìng)爭(zhēng)力在于“輕量、高效、靈活”,與EF Core相比,優(yōu)勢(shì)和適用場(chǎng)景差異明顯,具體對(duì)比如下:

對(duì)比維度DapperEF Core
性能極高,接近原生ADO.NET,反射開(kāi)銷(xiāo)極低中等,存在LINQ解析、跟蹤代理等性能開(kāi)銷(xiāo)
SQL控制力完全可控,需手動(dòng)編寫(xiě)SQL,支持復(fù)雜查詢(xún)、存儲(chǔ)過(guò)程自動(dòng)生成SQL,復(fù)雜查詢(xún)需寫(xiě)原生SQL或復(fù)雜LINQ,靈活性較弱
學(xué)習(xí)成本低,API簡(jiǎn)潔,核心方法僅10+個(gè),依賴(lài)SQL基礎(chǔ)高,需掌握LINQ、EF Core映射規(guī)則、遷移、跟蹤機(jī)制等
適用場(chǎng)景高性能需求、復(fù)雜SQL查詢(xún)、存儲(chǔ)過(guò)程、老項(xiàng)目改造、微服務(wù)快速開(kāi)發(fā)、簡(jiǎn)單CRUD、需要ORM抽象、團(tuán)隊(duì)熟悉LINQ
侵入性無(wú)侵入,實(shí)體類(lèi)無(wú)需繼承任何接口、標(biāo)注任何特性有侵入,實(shí)體類(lèi)需標(biāo)注特性(如[Key]),依賴(lài)EF Core核心包
補(bǔ)充:選型建議——小型項(xiàng)目、快速開(kāi)發(fā)選EF Core;高性能、復(fù)雜SQL、微服務(wù)選Dapper;實(shí)際項(xiàng)目中也可混合使用(如核心查詢(xún)用Dapper,簡(jiǎn)單CRUD用EF Core)。

3. 版本與安裝

Dapper最新穩(wěn)定版本為2.0+,安裝方式極其簡(jiǎn)單,通過(guò)NuGet安裝核心包即可,根據(jù)數(shù)據(jù)庫(kù)類(lèi)型選擇對(duì)應(yīng)擴(kuò)展包:

  • 核心包(必裝):Install-Package Dapper(.NET CLI:dotnet add package Dapper
  • 數(shù)據(jù)庫(kù)擴(kuò)展包(按需安裝):
    • SQL Server:無(wú)需額外擴(kuò)展(依賴(lài)System.Data.SqlClient)
    • MySQL:Install-Package MySqlConnector(推薦,替代舊版MySql.Data)
    • Oracle:Install-Package Oracle.ManagedDataAccess.Core

安裝完成后,引入核心命名空間即可使用:using Dapper;

二、基礎(chǔ)用法:從零開(kāi)始使用Dapper(核心CRUD)

Dapper的核心是擴(kuò)展方法——它通過(guò)擴(kuò)展IDbConnection接口,提供了一系列簡(jiǎn)潔的方法(如Query、Execute),無(wú)需創(chuàng)建復(fù)雜對(duì)象,即可完成數(shù)據(jù)訪問(wèn)操作?;A(chǔ)用法圍繞“連接字符串→創(chuàng)建連接→執(zhí)行SQL→映射結(jié)果”展開(kāi),以下是完整示例。

1. 準(zhǔn)備工作:實(shí)體類(lèi)與數(shù)據(jù)庫(kù)

以SQL Server為例,創(chuàng)建數(shù)據(jù)庫(kù)表和對(duì)應(yīng)實(shí)體類(lèi)(Dapper無(wú)需實(shí)體類(lèi)與表嚴(yán)格對(duì)應(yīng),靈活度極高):

-- 創(chuàng)建用戶(hù)表
CREATE TABLE [dbo].[Users] (
    [Id] INT IDENTITY(1,1) NOT NULL PRIMARY KEY,
    [UserName] NVARCHAR(50) NOT NULL,
    [Age] INT NOT NULL,
    [Email] NVARCHAR(100) NULL,
    [CreateTime] DATETIME NOT NULL DEFAULT GETDATE()
)
// 實(shí)體類(lèi)(無(wú)侵入,無(wú)需繼承任何接口、標(biāo)注特性)
public class User
{
    public int Id { get; set; }
    public string UserName { get; set; }
    public int Age { get; set; }
    public string Email { get; set; }
    public DateTime CreateTime { get; set; }
}

2. 核心基礎(chǔ):創(chuàng)建數(shù)據(jù)庫(kù)連接

Dapper依賴(lài)IDbConnection接口,需先創(chuàng)建數(shù)據(jù)庫(kù)連接(推薦使用using語(yǔ)句自動(dòng)釋放連接,避免連接泄漏):

using System.Data;
using System.Data.SqlClient;
using Dapper;
// 1. 連接字符串(建議放在配置文件中,此處簡(jiǎn)化)
string connectionString = "Data Source=.;Initial Catalog=DapperDemo;User ID=sa;Password=123456;";
// 2. 使用using語(yǔ)句創(chuàng)建連接(自動(dòng)打開(kāi)、關(guān)閉連接)
using (IDbConnection conn = new SqlConnection(connectionString))
{
    // 打開(kāi)連接(Dapper的部分方法會(huì)自動(dòng)打開(kāi)連接,建議手動(dòng)打開(kāi),提升性能)
    conn.Open();
    // 執(zhí)行SQL操作(后續(xù)講解)
}

【關(guān)鍵提醒】:using語(yǔ)句會(huì)自動(dòng)釋放連接,無(wú)需手動(dòng)調(diào)用conn.Close()conn.Dispose();手動(dòng)打開(kāi)連接(conn.Open())可避免Dapper自動(dòng)打開(kāi)/關(guān)閉連接的額外開(kāi)銷(xiāo),尤其適合批量操作。

3. 核心CRUD操作(重點(diǎn))

Dapper的核心方法集中在IDbConnection的擴(kuò)展方法中,最常用的有Query(查詢(xún))、Execute(執(zhí)行增刪改)、QueryFirstOrDefault(查詢(xún)單個(gè)結(jié)果)等,以下是完整CRUD示例。

(1)新增(Insert)

使用Execute方法執(zhí)行插入SQL,支持參數(shù)化查詢(xún)(防止SQL注入,必用):

// 準(zhǔn)備新增數(shù)據(jù)
var newUser = new User
{
    UserName = "張三",
    Age = 25,
    Email = "zhangsan@163.com",
    CreateTime = DateTime.Now
};
// 插入SQL(參數(shù)化,@UserName對(duì)應(yīng)實(shí)體類(lèi)的屬性名)
string insertSql = @"INSERT INTO Users (UserName, Age, Email, CreateTime)
                    VALUES (@UserName, @Age, @Email, @CreateTime);
                    SELECT SCOPE_IDENTITY();"; // 返回自增ID
using (IDbConnection conn = new SqlConnection(connectionString))
{
    conn.Open();
    // 執(zhí)行插入,返回自增ID(Convert.ToInt32轉(zhuǎn)換,適配SQL Server)
    int newUserId = conn.ExecuteScalar<int>(insertSql, newUser);
    Console.WriteLine($"新增用戶(hù)成功,ID:{newUserId}");
}

關(guān)鍵說(shuō)明:

  • ExecuteScalar<T>:執(zhí)行SQL并返回單個(gè)結(jié)果(如自增ID),泛型T指定返回值類(lèi)型。
  • 參數(shù)化查詢(xún):Dapper會(huì)自動(dòng)將實(shí)體類(lèi)的屬性與SQL中的@參數(shù)名對(duì)應(yīng),無(wú)需手動(dòng)拼接SQL,徹底避免SQL注入。

(2)查詢(xún)(Select)

查詢(xún)是Dapper最常用的操作,核心方法是Query<T>(查詢(xún)多個(gè)結(jié)果)和QueryFirstOrDefault<T>(查詢(xún)單個(gè)結(jié)果),支持復(fù)雜SQL、聯(lián)表查詢(xún)。

// 示例1:查詢(xún)所有用戶(hù)(返回List<User>)
string selectAllSql = "SELECT * FROM Users";
using (IDbConnection conn = new SqlConnection(connectionString))
{
    conn.Open();
    // Query<T>返回IEnumerable<T>,可直接轉(zhuǎn)換為L(zhǎng)ist
    List<User> userList = conn.Query<User>(selectAllSql).ToList();
    foreach (var user in userList)
    {
        Console.WriteLine($"ID:{user.Id},姓名:{user.UserName}");
    }
}
// 示例2:根據(jù)ID查詢(xún)單個(gè)用戶(hù)(查詢(xún)不到返回null)
int userId = 1;
string selectOneSql = "SELECT * FROM Users WHERE Id = @Id";
using (IDbConnection conn = new SqlConnection(connectionString))
{
    conn.Open();
    // QueryFirstOrDefault<T>:查詢(xún)第一個(gè)結(jié)果,無(wú)結(jié)果返回null
    User user = conn.QueryFirstOrDefault<User>(selectOneSql, new { Id = userId });
    if (user != null)
    {
        Console.WriteLine($"查詢(xún)到用戶(hù):{user.UserName}");
    }
    else
    {
        Console.WriteLine("未查詢(xún)到用戶(hù)");
    }
}
// 示例3:條件查詢(xún)(多參數(shù))
int minAge = 20;
string emailLike = "%163.com%";
string selectConditionSql = "SELECT * FROM Users WHERE Age >= @MinAge AND Email LIKE @EmailLike";
using (IDbConnection conn = new SqlConnection(connectionString))
{
    conn.Open();
    // 多參數(shù):使用匿名對(duì)象傳遞
    var users = conn.Query<User>(selectConditionSql, new { MinAge = minAge, EmailLike = emailLike }).ToList();
}

(3)修改(Update)

使用Execute方法執(zhí)行更新SQL,返回受影響的行數(shù):

// 準(zhǔn)備修改數(shù)據(jù)
int updateId = 1;
var updateUser = new User
{
    UserName = "張三_修改",
    Age = 26,
    Email = "zhangsan_update@163.com"
};
string updateSql = @"UPDATE Users 
                    SET UserName = @UserName, Age = @Age, Email = @Email
                    WHERE Id = @Id";
using (IDbConnection conn = new SqlConnection(connectionString))
{
    conn.Open();
    // 傳遞多個(gè)參數(shù)(匿名對(duì)象)
    int affectedRows = conn.Execute(updateSql, new { updateUser.UserName, updateUser.Age, updateUser.Email, Id = updateId });
    Console.WriteLine($"修改成功,受影響行數(shù):{affectedRows}");
}

(4)刪除(Delete)

同樣使用Execute方法,返回受影響的行數(shù):

int deleteId = 1;
string deleteSql = "DELETE FROM Users WHERE Id = @Id";
using (IDbConnection conn = new SqlConnection(connectionString))
{
    conn.Open();
    int affectedRows = conn.Execute(deleteSql, new { Id = deleteId });
    Console.WriteLine($"刪除成功,受影響行數(shù):{affectedRows}");
}

4. 核心API總結(jié)(必記)

Dapper的API極其簡(jiǎn)潔,核心方法僅10+個(gè),以下是最常用的API,記熟即可覆蓋90%的場(chǎng)景:

API方法作用返回值
Query(sql, param)執(zhí)行查詢(xún)SQL,返回多個(gè)結(jié)果IEnumerable
QueryFirstOrDefault(sql, param)執(zhí)行查詢(xún)SQL,返回第一個(gè)結(jié)果,無(wú)結(jié)果返回nullT
QuerySingleOrDefault(sql, param)執(zhí)行查詢(xún)SQL,返回單個(gè)結(jié)果(若多個(gè)結(jié)果拋出異常)T
Execute(sql, param)執(zhí)行增刪改SQL、存儲(chǔ)過(guò)程int(受影響行數(shù))
ExecuteScalar(sql, param)執(zhí)行SQL,返回單個(gè)值(如自增ID、統(tǒng)計(jì)結(jié)果)T
QueryMultiple(sql, param)執(zhí)行多個(gè)SQL語(yǔ)句,返回多個(gè)結(jié)果集SqlMapper.GridReader

三、核心特性:Dapper進(jìn)階用法(深度重點(diǎn))

基礎(chǔ)CRUD只能滿(mǎn)足簡(jiǎn)單場(chǎng)景,實(shí)際項(xiàng)目中常遇到聯(lián)表查詢(xún)、多結(jié)果集、存儲(chǔ)過(guò)程、事務(wù)處理等復(fù)雜需求,以下是Dapper最核心的進(jìn)階特性,也是體現(xiàn)其靈活性和強(qiáng)大性的關(guān)鍵。

1. 參數(shù)化查詢(xún)?cè)斀猓ǚ繱QL注入,必掌握)

SQL注入是Web開(kāi)發(fā)中最常見(jiàn)的安全隱患,Dapper的參數(shù)化查詢(xún)徹底解決了這一問(wèn)題,支持3種參數(shù)傳遞方式,適配不同場(chǎng)景:

(1)匿名對(duì)象參數(shù)(最常用)

// 適用于參數(shù)較少的場(chǎng)景
string sql = "SELECT * FROM Users WHERE Age >= @MinAge AND UserName LIKE @UserName";
var users = conn.Query<User>(sql, new { MinAge = 20, UserName = "%張%" }).ToList();

(2)實(shí)體類(lèi)參數(shù)(適用于參數(shù)較多的場(chǎng)景)

// 實(shí)體類(lèi)的屬性名與SQL中的@參數(shù)名一致即可
var param = new User { Age = 20, UserName = "張三" };
string sql = "SELECT * FROM Users WHERE Age = @Age AND UserName = @UserName";
var user = conn.QueryFirstOrDefault<User>(sql, param);

(3)DynamicParameters參數(shù)(適用于動(dòng)態(tài)參數(shù)、多參數(shù)場(chǎng)景)

當(dāng)參數(shù)個(gè)數(shù)不確定、需要?jiǎng)討B(tài)添加參數(shù)時(shí),使用DynamicParameters類(lèi):

var param = new DynamicParameters();
param.Add("@MinAge", 20); // 添加參數(shù)
param.Add("@MaxAge", 30);
param.Add("@UserName", "%張%", DbType.String, ParameterDirection.Input, 50); // 指定參數(shù)類(lèi)型、方向、長(zhǎng)度
string sql = "SELECT * FROM Users WHERE Age BETWEEN @MinAge AND @MaxAge AND UserName LIKE @UserName";
var users = conn.Query<User>(sql, param).ToList();

2. 多結(jié)果集查詢(xún)(QueryMultiple)

實(shí)際項(xiàng)目中,經(jīng)常需要一次性執(zhí)行多個(gè)SQL語(yǔ)句,獲取多個(gè)結(jié)果集(如同時(shí)查詢(xún)用戶(hù)表和訂單表),Dapper的QueryMultiple方法可高效實(shí)現(xiàn),避免多次數(shù)據(jù)庫(kù)連接:

// 一次性執(zhí)行兩個(gè)SQL語(yǔ)句,獲取兩個(gè)結(jié)果集
string sql = @"SELECT * FROM Users WHERE Id = @Id;
              SELECT * FROM Orders WHERE UserId = @Id;";
using (IDbConnection conn = new SqlConnection(connectionString))
{
    conn.Open();
    // 執(zhí)行多結(jié)果集查詢(xún)
    using (var multi = conn.QueryMultiple(sql, new { Id = 1 }))
    {
        // 讀取第一個(gè)結(jié)果集(用戶(hù))
        User user = multi.Read<User>().FirstOrDefault();
        // 讀取第二個(gè)結(jié)果集(該用戶(hù)的訂單)
        List<Order> orders = multi.Read<Order>().ToList();
        // 關(guān)聯(lián)數(shù)據(jù)
        if (user != null)
        {
            user.Orders = orders;
        }
    }
}

關(guān)鍵說(shuō)明:Read<T>()方法用于讀取當(dāng)前結(jié)果集,每次調(diào)用讀取下一個(gè)結(jié)果集,順序與SQL語(yǔ)句順序一致。

3. 聯(lián)表查詢(xún)與實(shí)體映射(復(fù)雜場(chǎng)景)

聯(lián)表查詢(xún)是實(shí)際項(xiàng)目中最常見(jiàn)的復(fù)雜查詢(xún)場(chǎng)景,Dapper支持兩種映射方式:“手動(dòng)映射”和“自動(dòng)映射”,適配不同復(fù)雜度的聯(lián)表需求。

(1)簡(jiǎn)單聯(lián)表(自動(dòng)映射)

當(dāng)聯(lián)表查詢(xún)的結(jié)果字段與實(shí)體類(lèi)屬性名一致時(shí),可直接映射:

-- 聯(lián)表查詢(xún):用戶(hù)表 + 角色表
SELECT u.Id, u.UserName, u.Age, r.RoleName
FROM Users u
LEFT JOIN Roles r ON u.RoleId = r.Id
WHERE u.Id = @Id
// 定義包含聯(lián)表字段的實(shí)體類(lèi)(可新增DTO)
public class UserWithRole
{
    public int Id { get; set; }
    public string UserName { get; set; }
    public int Age { get; set; }
    public string RoleName { get; set; } // 角色表的字段
}
// 自動(dòng)映射聯(lián)表結(jié)果
string sql = @"SELECT u.Id, u.UserName, u.Age, r.RoleName
              FROM Users u
              LEFT JOIN Roles r ON u.RoleId = r.Id
              WHERE u.Id = @Id";
UserWithRole user = conn.QueryFirstOrDefault<UserWithRole>(sql, new { Id = 1 });

(2)復(fù)雜聯(lián)表(手動(dòng)映射)

當(dāng)聯(lián)表結(jié)果需要映射到嵌套實(shí)體(如用戶(hù)包含角色列表)時(shí),需使用Dapper的Query<T1, T2, T3>方法手動(dòng)指定映射邏輯:

// 實(shí)體類(lèi)(用戶(hù)包含角色列表)
public class User
{
    public int Id { get; set; }
    public string UserName { get; set; }
    public List<Role> Roles { get; set; } = new List<Role>();
}
public class Role
{
    public int Id { get; set; }
    public string RoleName { get; set; }
}
// 聯(lián)表查詢(xún)SQL
string sql = @"SELECT u.Id, u.UserName, r.Id AS RoleId, r.RoleName
              FROM Users u
              LEFT JOIN UserRoles ur ON u.Id = ur.UserId
              LEFT JOIN Roles r ON ur.RoleId = r.Id
              WHERE u.Id = @Id";
// 手動(dòng)映射:將查詢(xún)結(jié)果映射到User和Role,關(guān)聯(lián)角色列表
var users = conn.Query<User, Role, User>(
    sql,
    (user, role) => // 映射委托:第一個(gè)參數(shù)是主實(shí)體,第二個(gè)是關(guān)聯(lián)實(shí)體
    {
        if (role != null)
        {
            user.Roles.Add(role);
        }
        return user;
    },
    param: new { Id = 1 },
    splitOn: "RoleId" // 拆分字段:指定從哪個(gè)字段開(kāi)始拆分第二個(gè)實(shí)體(Role)
).Distinct().ToList(); // Distinct去重(避免用戶(hù)被重復(fù)返回)

關(guān)鍵說(shuō)明:splitOn參數(shù)是手動(dòng)映射的核心,用于指定“主實(shí)體”和“關(guān)聯(lián)實(shí)體”的拆分字段,必須是查詢(xún)結(jié)果中存在的字段(如RoleId)。

4. 存儲(chǔ)過(guò)程調(diào)用(企業(yè)級(jí)項(xiàng)目常用)

企業(yè)級(jí)項(xiàng)目中,大量復(fù)雜業(yè)務(wù)邏輯會(huì)封裝在存儲(chǔ)過(guò)程中,Dapper對(duì)存儲(chǔ)過(guò)程的支持非常完善,支持輸入?yún)?shù)、輸出參數(shù)、返回值。

示例:調(diào)用存儲(chǔ)過(guò)程查詢(xún)用戶(hù)

-- 創(chuàng)建存儲(chǔ)過(guò)程
CREATE PROCEDURE GetUserById
    @Id INT -- 輸入?yún)?shù)
AS
BEGIN
    SELECT * FROM Users WHERE Id = @Id
END
// 調(diào)用存儲(chǔ)過(guò)程(指定commandType為StoredProcedure)
int userId = 1;
var user = conn.QueryFirstOrDefault<User>(
    "GetUserById", // 存儲(chǔ)過(guò)程名稱(chēng)
    new { Id = userId }, // 輸入?yún)?shù)
    commandType: CommandType.StoredProcedure // 關(guān)鍵:指定為存儲(chǔ)過(guò)程
);

示例:調(diào)用帶輸出參數(shù)的存儲(chǔ)過(guò)程

-- 創(chuàng)建帶輸出參數(shù)的存儲(chǔ)過(guò)程
CREATE PROCEDURE GetUserCountByAge
    @MinAge INT, -- 輸入?yún)?shù)
    @Count INT OUTPUT -- 輸出參數(shù)
AS
BEGIN
    SELECT @Count = COUNT(*) FROM Users WHERE Age >= @MinAge
END
// 使用DynamicParameters傳遞輸入/輸出參數(shù)
var param = new DynamicParameters();
param.Add("@MinAge", 20); // 輸入?yún)?shù)
param.Add("@Count", 0, DbType.Int32, ParameterDirection.Output); // 輸出參數(shù)(初始值0)
// 調(diào)用存儲(chǔ)過(guò)程
conn.Execute(
    "GetUserCountByAge",
    param,
    commandType: CommandType.StoredProcedure
);
// 獲取輸出參數(shù)的值
int count = param.Get<int>("@Count");
Console.WriteLine($"年齡>=20的用戶(hù)數(shù)量:{count}");

5. 事務(wù)處理(保證數(shù)據(jù)一致性)

當(dāng)需要執(zhí)行多個(gè)SQL操作(如新增用戶(hù)同時(shí)新增角色關(guān)聯(lián))時(shí),需使用事務(wù)保證數(shù)據(jù)一致性,Dapper支持兩種事務(wù)方式:IDbTransaction(原生事務(wù))和TransactionScope(分布式事務(wù))。

(1)原生事務(wù)(IDbTransaction)

using (IDbConnection conn = new SqlConnection(connectionString))
{
    conn.Open();
    // 開(kāi)啟事務(wù)
    using (IDbTransaction tran = conn.BeginTransaction())
    {
        try
        {
            // 執(zhí)行第一個(gè)SQL(新增用戶(hù))
            string insertUserSql = "INSERT INTO Users (UserName, Age) VALUES (@UserName, @Age); SELECT SCOPE_IDENTITY()";
            int userId = conn.ExecuteScalar<int>(insertUserSql, new { UserName = "李四", Age = 28 }, transaction: tran);
            // 執(zhí)行第二個(gè)SQL(新增用戶(hù)角色關(guān)聯(lián))
            string insertUserRoleSql = "INSERT INTO UserRoles (UserId, RoleId) VALUES (@UserId, @RoleId)";
            conn.Execute(insertUserRoleSql, new { UserId = userId, RoleId = 1 }, transaction: tran);
            // 提交事務(wù)
            tran.Commit();
            Console.WriteLine("事務(wù)執(zhí)行成功");
        }
        catch (Exception ex)
        {
            // 回滾事務(wù)(出現(xiàn)異常時(shí))
            tran.Rollback();
            Console.WriteLine($"事務(wù)執(zhí)行失?。簕ex.Message}");
        }
    }
}

(2)分布式事務(wù)(TransactionScope)

當(dāng)需要跨多個(gè)數(shù)據(jù)庫(kù)、多個(gè)連接執(zhí)行事務(wù)時(shí),使用TransactionScope(需引用System.Transactions包):

// 需安裝NuGet包:System.Transactions
using (var scope = new TransactionScope())
{
    // 第一個(gè)數(shù)據(jù)庫(kù)連接
    using (IDbConnection conn1 = new SqlConnection(connectionString1))
    {
        conn1.Open();
        conn1.Execute("INSERT INTO Users (UserName) VALUES (@UserName)", new { UserName = "王五" });
    }
    // 第二個(gè)數(shù)據(jù)庫(kù)連接(跨庫(kù)事務(wù))
    using (IDbConnection conn2 = new SqlConnection(connectionString2))
    {
        conn2.Open();
        conn2.Execute("INSERT INTO Orders (OrderNo) VALUES (@OrderNo)", new { OrderNo = "20240520001" });
    }
    // 提交事務(wù)(所有操作成功才提交)
    scope.Complete();
}

四、高級(jí)技巧:Dapper擴(kuò)展與最佳實(shí)踐

Dapper本身輕量,但通過(guò)擴(kuò)展包和最佳實(shí)踐,可進(jìn)一步提升開(kāi)發(fā)效率,適配更復(fù)雜的項(xiàng)目場(chǎng)景。

1. Dapper擴(kuò)展包推薦(必裝)

Dapper官方提供了多個(gè)擴(kuò)展包,簡(jiǎn)化重復(fù)操作,最常用的有兩個(gè):

(1)Dapper.Contrib(簡(jiǎn)化CRUD,無(wú)需寫(xiě)SQL)

Dapper.Contrib提供了InsertUpdate、DeleteGet等方法,無(wú)需手動(dòng)編寫(xiě)SQL,自動(dòng)生成基礎(chǔ)CRUD語(yǔ)句,適合簡(jiǎn)單CRUD場(chǎng)景:

// 安裝擴(kuò)展包:Install-Package Dapper.Contrib
using Dapper.Contrib.Extensions;
// 實(shí)體類(lèi)需標(biāo)注[Table]特性(指定數(shù)據(jù)庫(kù)表名,可選)
[Table("Users")]
public class User
{
    [Key] // 標(biāo)注主鍵(自增主鍵需標(biāo)注)
    public int Id { get; set; }
    public string UserName { get; set; }
    public int Age { get; set; }
}
// 簡(jiǎn)化CRUD操作(無(wú)需寫(xiě)SQL)
using (IDbConnection conn = new SqlConnection(connectionString))
{
    conn.Open();
    // 新增
    int newId = conn.Insert<int>(new User { UserName = "趙六", Age = 30 });
    // 根據(jù)ID查詢(xún)
    User user = conn.Get<User>(newId);
    // 修改
    user.UserName = "趙六_修改";
    conn.Update(user);
    // 刪除
    conn.Delete(user);
    // 查詢(xún)所有
    List<User> users = conn.GetAll<User>().ToList();
}

(2)Dapper.SqlBuilder(動(dòng)態(tài)構(gòu)建SQL)

當(dāng)SQL語(yǔ)句需要根據(jù)條件動(dòng)態(tài)拼接(如多條件篩選、動(dòng)態(tài)排序)時(shí),使用Dapper.SqlBuilder可避免手動(dòng)拼接SQL的繁瑣和風(fēng)險(xiǎn):

// 安裝擴(kuò)展包:Install-Package Dapper.SqlBuilder
using Dapper.SqlBuilder;
// 構(gòu)建動(dòng)態(tài)SQL
var builder = new SqlBuilder();
// 基礎(chǔ)SQL
var template = builder.AddTemplate(@"SELECT * FROM Users /**where** /**orderby**");
// 動(dòng)態(tài)添加WHERE條件
int? minAge = 20;
string userName = "張";
if (minAge.HasValue)
{
    builder.Where("Age >= @MinAge", new { MinAge = minAge });
}
if (!string.IsNullOrEmpty(userName))
{
    builder.Where("UserName LIKE @UserName", new { UserName = $"%{userName}%" });
}
// 動(dòng)態(tài)添加排序
builder.OrderBy("CreateTime DESC");
// 執(zhí)行查詢(xún)
using (IDbConnection conn = new SqlConnection(connectionString))
{
    conn.Open();
    var users = conn.Query<User>(template.RawSql, template.Parameters).ToList();
}

2. 項(xiàng)目中Dapper的最佳實(shí)踐

在實(shí)際項(xiàng)目中,合理使用Dapper可提升代碼可維護(hù)性和性能,以下是企業(yè)級(jí)項(xiàng)目中的最佳實(shí)踐:

(1)封裝Dapper工具類(lèi)(全局單例)

將數(shù)據(jù)庫(kù)連接、基礎(chǔ)CRUD操作封裝為工具類(lèi),避免重復(fù)代碼,統(tǒng)一管理連接字符串和異常處理:

public static class DapperHelper
{
    // 連接字符串(從配置文件讀取,推薦使用IConfiguration)
    private static readonly string _connectionString = "Data Source=.;Initial Catalog=DapperDemo;User ID=sa;Password=123456;";
    // 執(zhí)行增刪改操作
    public static int Execute(string sql, object param = null, IDbTransaction transaction = null)
    {
        using (IDbConnection conn = new SqlConnection(_connectionString))
        {
            conn.Open();
            return conn.Execute(sql, param, transaction);
        }
    }
    // 執(zhí)行查詢(xún)操作(返回多個(gè)結(jié)果)
    public static List<T> Query<T>(string sql, object param = null, IDbTransaction transaction = null)
    {
        using (IDbConnection conn = new SqlConnection(_connectionString))
        {
            conn.Open();
            return conn.Query<T>(sql, param, transaction).ToList();
        }
    }
    // 執(zhí)行查詢(xún)操作(返回單個(gè)結(jié)果)
    public static T QueryFirstOrDefault<T>(string sql, object param = null, IDbTransaction transaction = null)
    {
        using (IDbConnection conn = new SqlConnection(_connectionString))
        {
            conn.Open();
            return conn.QueryFirstOrDefault<T>(sql, param, transaction);
        }
    }
    // 其他方法...
}

(2)分層架構(gòu)中使用Dapper(DAL層封裝)

在分層架構(gòu)(UI→BLL→DAL)中,將Dapper操作封裝在DAL層,BLL層調(diào)用DAL層方法,解耦業(yè)務(wù)邏輯與數(shù)據(jù)訪問(wèn):

// DAL層(數(shù)據(jù)訪問(wèn)層)
public class UserDal
{
    public User GetById(int id)
    {
        string sql = "SELECT * FROM Users WHERE Id = @Id";
        return DapperHelper.QueryFirstOrDefault<User>(sql, new { Id = id });
    }
    public int Add(User user)
    {
        string sql = "INSERT INTO Users (UserName, Age, Email) VALUES (@UserName, @Age, @Email); SELECT SCOPE_IDENTITY()";
        return DapperHelper.ExecuteScalar<int>(sql, user);
    }
    // 其他方法...
}
// BLL層(業(yè)務(wù)邏輯層)
public class UserBll
{
    private readonly UserDal _userDal = new UserDal();
    public User GetUserById(int id)
    {
        // 業(yè)務(wù)邏輯處理
        if (id <= 0)
        {
            throw new ArgumentException("用戶(hù)ID無(wú)效");
        }
        return _userDal.GetById(id);
    }
    // 其他方法...
}

(3)SQL語(yǔ)句優(yōu)化(提升性能)

  • 避免SELECT *:只查詢(xún)需要的字段,減少數(shù)據(jù)傳輸量和內(nèi)存占用。
  • 使用索引:查詢(xún)條件(WHERE、JOIN)的字段需建立索引,提升查詢(xún)速度。
  • 批量操作:批量新增/修改時(shí),使用Execute方法一次性執(zhí)行,避免循環(huán)調(diào)用(如批量新增1000條數(shù)據(jù),拼接SQL一次性執(zhí)行)。
  • 分頁(yè)查詢(xún):使用分頁(yè)SQL(如SQL Server的OFFSET/FETCH),避免查詢(xún)?nèi)繑?shù)據(jù)再分頁(yè)。

五、性能優(yōu)化:讓Dapper跑得更快(核心重點(diǎn))

Dapper本身性能接近原生ADO.NET,但如果使用不當(dāng),仍會(huì)出現(xiàn)性能瓶頸,以下是最實(shí)用的性能優(yōu)化技巧,覆蓋“連接管理、SQL優(yōu)化、映射優(yōu)化”三個(gè)核心維度。

1. 連接管理優(yōu)化(最關(guān)鍵)

  • 使用連接池:.NET默認(rèn)啟用數(shù)據(jù)庫(kù)連接池(連接字符串中Pooling=true,默認(rèn)開(kāi)啟),無(wú)需手動(dòng)管理連接池,避免頻繁創(chuàng)建/關(guān)閉連接。
  • 手動(dòng)打開(kāi)連接:Dapper的Query、Execute方法會(huì)自動(dòng)打開(kāi)連接,但建議手動(dòng)調(diào)用conn.Open(),尤其是批量操作,避免多次打開(kāi)/關(guān)閉連接的開(kāi)銷(xiāo)。
  • 避免長(zhǎng)連接:不要長(zhǎng)時(shí)間持有數(shù)據(jù)庫(kù)連接(如全局連接),使用using語(yǔ)句自動(dòng)釋放,避免連接泄漏。

2. 映射優(yōu)化(減少反射開(kāi)銷(xiāo))

Dapper的映射依賴(lài)反射,雖然反射開(kāi)銷(xiāo)極低,但高頻調(diào)用場(chǎng)景下仍需優(yōu)化:

  • 使用TypeHandler自定義類(lèi)型映射:對(duì)于自定義類(lèi)型(如枚舉、DateTimeOffset),自定義TypeHandler,避免Dapper自動(dòng)反射解析。
  • 避免匿名對(duì)象映射:高頻查詢(xún)場(chǎng)景下,使用實(shí)體類(lèi)映射,比匿名對(duì)象映射更快(匿名對(duì)象需動(dòng)態(tài)創(chuàng)建類(lèi)型)。
  • 啟用Dapper緩存:Dapper會(huì)自動(dòng)緩存SQL語(yǔ)句的解析結(jié)果,無(wú)需手動(dòng)配置,確保SQL語(yǔ)句格式一致(避免重復(fù)解析)。

3. SQL優(yōu)化(核心中的核心)

  • 參數(shù)化查詢(xún):不僅防SQL注入,還能讓數(shù)據(jù)庫(kù)緩存查詢(xún)計(jì)劃,提升重復(fù)查詢(xún)的性能。
  • 避免復(fù)雜子查詢(xún):復(fù)雜子查詢(xún)會(huì)增加數(shù)據(jù)庫(kù)計(jì)算開(kāi)銷(xiāo),盡量使用JOIN替代子查詢(xún)。
  • 批量操作優(yōu)化:批量新增/修改時(shí),使用Execute方法一次性執(zhí)行多條SQL(用分號(hào)分隔),或使用表值參數(shù)(SQL Server),避免循環(huán)調(diào)用。
  • 分頁(yè)優(yōu)化:使用數(shù)據(jù)庫(kù)原生分頁(yè)(如SQL Server的OFFSET/FETCH、MySQL的LIMIT),避免查詢(xún)?nèi)繑?shù)據(jù)再分頁(yè)。

4. 高頻場(chǎng)景優(yōu)化示例

高頻查詢(xún)場(chǎng)景(如首頁(yè)列表查詢(xún)),優(yōu)化方案:

// 優(yōu)化前:查詢(xún)?nèi)孔侄?,無(wú)分頁(yè)
string sql = "SELECT * FROM Users WHERE Age >= 20";
var users = DapperHelper.Query<User>(sql);
// 優(yōu)化后:只查需要的字段,分頁(yè)查詢(xún),使用索引
string sql = "SELECT Id, UserName, Age FROM Users WHERE Age >= @MinAge ORDER BY CreateTime DESC OFFSET @PageSize * (@PageIndex - 1) ROWS FETCH NEXT @PageSize ROWS ONLY";
var users = DapperHelper.Query<User>(sql, new { MinAge = 20, PageIndex = 1, PageSize = 10 });

六、避坑指南:常見(jiàn)問(wèn)題與解決方案

Dapper用法簡(jiǎn)單,但實(shí)際項(xiàng)目中仍會(huì)遇到一些坑,以下是最常見(jiàn)的問(wèn)題及解決方案,幫你避免踩坑。

1. 問(wèn)題1:參數(shù)名與實(shí)體類(lèi)屬性名不一致,映射失敗

【原因】:Dapper默認(rèn)按“屬性名=參數(shù)名”映射,若SQL中的參數(shù)名與實(shí)體類(lèi)屬性名不一致,會(huì)導(dǎo)致映射失?。ǚ祷豱ull或默認(rèn)值)。

【解決方案】:

// 方案1:修改SQL參數(shù)名,與實(shí)體類(lèi)屬性名一致
string sql = "SELECT * FROM Users WHERE UserName = @UserName"; // 與實(shí)體類(lèi)的UserName一致
// 方案2:使用別名,讓查詢(xún)結(jié)果字段與實(shí)體類(lèi)屬性名一致
string sql = "SELECT UserName AS Name FROM Users"; // 實(shí)體類(lèi)屬性名為Name
// 方案3:手動(dòng)映射(復(fù)雜場(chǎng)景)
var user = conn.QueryFirstOrDefault<User>(
    "SELECT UserName AS Name FROM Users WHERE Id = @Id",
    new { Id = 1 }
);

2. 問(wèn)題2:自增主鍵返回失?。⊿QL Server)

【原因】:插入SQL未添加SELECT SCOPE_IDENTITY(),或使用Execute方法而非ExecuteScalar。

【解決方案】:

// 正確寫(xiě)法:插入后返回自增ID,使用ExecuteScalar
string sql = "INSERT INTO Users (UserName) VALUES (@UserName); SELECT SCOPE_IDENTITY()";
int newId = conn.ExecuteScalar<int>(sql, new { UserName = "張三" });

3. 問(wèn)題3:多結(jié)果集讀取順序錯(cuò)誤,導(dǎo)致映射失敗

【原因】:Read<T>方法的調(diào)用順序與SQL語(yǔ)句中結(jié)果集的順序不一致。

【解決方案】:確保Read<T>的順序與SQL語(yǔ)句中多個(gè)SQL的順序一致:

// SQL順序:先查Users,再查Orders
string sql = "SELECT * FROM Users; SELECT * FROM Orders;";
using (var multi = conn.QueryMultiple(sql))
{
    var users = multi.Read<User>().ToList(); // 先讀Users(與SQL順序一致)
    var orders = multi.Read<Order>().ToList(); // 再讀Orders
}

4. 問(wèn)題4:事務(wù)提交后,數(shù)據(jù)未生效

【原因】:執(zhí)行SQL時(shí)未指定事務(wù)對(duì)象,導(dǎo)致SQL執(zhí)行在事務(wù)之外。

【解決方案】:執(zhí)行SQL時(shí),將transaction參數(shù)指定為開(kāi)啟的事務(wù)對(duì)象:

using (var tran = conn.BeginTransaction())
{
    // 關(guān)鍵:指定transaction參數(shù)
    conn.Execute(insertSql, param, transaction: tran);
    tran.Commit();
}

5. 問(wèn)題5:高頻調(diào)用時(shí)性能下降

【原因】:頻繁創(chuàng)建連接、SQL語(yǔ)句未緩存、未使用索引、查詢(xún)字段過(guò)多。

【解決方案】:

  • 使用連接池,手動(dòng)打開(kāi)連接,避免頻繁創(chuàng)建/關(guān)閉。
  • 確保SQL語(yǔ)句格式一致,讓Dapper緩存解析結(jié)果。
  • 查詢(xún)條件字段建立索引,只查詢(xún)需要的字段。

七、總結(jié)

Dapper的核心價(jià)值的是“輕量、高效、靈活”,它不追求“大而全”,而是專(zhuān)注于解決數(shù)據(jù)映射的核心痛點(diǎn),在保持原生SQL控制力的同時(shí),簡(jiǎn)化開(kāi)發(fā)流程。掌握以下核心要點(diǎn),即可在項(xiàng)目中高效使用Dapper:

  • 基礎(chǔ)核心:通過(guò)IDbConnection的擴(kuò)展方法(Query、Execute)執(zhí)行SQL,參數(shù)化查詢(xún)防SQL注入。
  • 進(jìn)階特性:多結(jié)果集(QueryMultiple)、聯(lián)表映射、存儲(chǔ)過(guò)程調(diào)用、事務(wù)處理,適配復(fù)雜場(chǎng)景。
  • 擴(kuò)展工具:Dapper.Contrib簡(jiǎn)化CRUD,Dapper.SqlBuilder動(dòng)態(tài)構(gòu)建SQL,提升開(kāi)發(fā)效率。
  • 性能優(yōu)化:合理管理連接、優(yōu)化SQL語(yǔ)句、減少反射開(kāi)銷(xiāo),發(fā)揮Dapper的性能優(yōu)勢(shì)。
  • 避坑關(guān)鍵:注意參數(shù)名與屬性名一致、自增主鍵返回方式、事務(wù)參數(shù)指定、多結(jié)果集讀取順序。

Dapper不是EF Core的替代品,而是互補(bǔ)品——在高性能、復(fù)雜SQL、微服務(wù)等場(chǎng)景中,Dapper是最優(yōu)選擇;在快速開(kāi)發(fā)、簡(jiǎn)單CRUD場(chǎng)景中,EF Core更便捷。合理選擇工具,才能讓開(kāi)發(fā)效率與系統(tǒng)性能達(dá)到平衡。

到此這篇關(guān)于C#常用類(lèi)庫(kù)Dapper詳解的文章就介紹到這了,更多相關(guān)C#常用類(lèi)庫(kù)Dapper內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • UGUI實(shí)現(xiàn)4位驗(yàn)證碼輸入

    UGUI實(shí)現(xiàn)4位驗(yàn)證碼輸入

    這篇文章主要為大家詳細(xì)介紹了UGUI實(shí)現(xiàn)4位驗(yàn)證碼輸入,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-12-12
  • C#實(shí)現(xiàn)批量重命名文件的項(xiàng)目實(shí)踐

    C#實(shí)現(xiàn)批量重命名文件的項(xiàng)目實(shí)踐

    文件重命名是一個(gè)在IT行業(yè)中常見(jiàn)的任務(wù),它涉及到對(duì)大量文件進(jìn)行系統(tǒng)性的命名更改,以便于更有效的文件管理和數(shù)據(jù)檢索,下面就來(lái)詳細(xì)的介紹一下C#實(shí)現(xiàn)批量重命名文件,感興趣的可以了解一下
    2026-05-05
  • C#繪制飛行棋地圖小程序

    C#繪制飛行棋地圖小程序

    這篇文章主要介紹了C#繪制飛行棋地圖小程序的相關(guān)資料,非常不錯(cuò),具有參考借鑒價(jià)值,對(duì)c飛行棋地圖相關(guān)知識(shí)感興趣的朋友一起看看吧
    2016-09-09
  • 基于c# 接口的實(shí)例詳解

    基于c# 接口的實(shí)例詳解

    本篇文章是對(duì)c#中的接口進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-06-06
  • C#實(shí)現(xiàn)高性能文件批量處理器的示例代碼

    C#實(shí)現(xiàn)高性能文件批量處理器的示例代碼

    文件批量處理器可以用于數(shù)字資產(chǎn)管理,數(shù)據(jù)遷移工程,日志文件處理和安全審計(jì)場(chǎng)景,本文將使用C#開(kāi)發(fā)一個(gè)高性能文件批量處理器,希望對(duì)大家有所幫助
    2025-03-03
  • C#自定義字符串替換Replace方法實(shí)例

    C#自定義字符串替換Replace方法實(shí)例

    這篇文章主要介紹了C#自定義字符串替換Replace方法,以?xún)蓚€(gè)不同實(shí)例對(duì)比分析了C#自定義字符串替換Replace的方法,是C#關(guān)于字符串操作的典型應(yīng)用,需要的朋友可以參考下
    2014-11-11
  • 如何在C# 中使用 FFmpeg.NET

    如何在C# 中使用 FFmpeg.NET

    這篇文章主要介紹了如何在C# 中使用 FFmpeg.NET,幫助大家更好的理解和學(xué)習(xí)使用c#,感興趣的朋友可以了解下
    2021-03-03
  • C#實(shí)現(xiàn)DataTable映射成Model的方法(附源碼)

    C#實(shí)現(xiàn)DataTable映射成Model的方法(附源碼)

    這篇文章主要介紹了C#實(shí)現(xiàn)DataTable映射成Model的方法,以實(shí)例形式較為詳細(xì)的分析了DataTable映射成Model的具體步驟與相關(guān)技巧,并附帶了完整實(shí)例源碼供讀者下載,需要的朋友可以參考下
    2015-11-11
  • C# 調(diào)用WebApi的實(shí)現(xiàn)

    C# 調(diào)用WebApi的實(shí)現(xiàn)

    這篇文章主要介紹了C# 調(diào)用WebApi的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-04-04
  • C# WCF簡(jiǎn)單入門(mén)圖文教程(VS2010版)

    C# WCF簡(jiǎn)單入門(mén)圖文教程(VS2010版)

    這篇文章主要介紹了WCF簡(jiǎn)單入門(mén)圖文教程,版本是VS2010版,幫助大家輕松學(xué)習(xí)了解DataContract、ServiceContract等特性,感興趣的小伙伴們可以參考一下
    2016-03-03

最新評(píng)論

扎鲁特旗| 广安市| 临清市| 尉氏县| 宜丰县| 琼中| 新乡县| 醴陵市| 中西区| 古浪县| 类乌齐县| 额尔古纳市| 民县| 四川省| 大埔县| 庄浪县| 苍溪县| 博乐市| 永吉县| 嵊泗县| 郸城县| 达日县| 昭苏县| 普陀区| 平凉市| 逊克县| 蓝田县| 深圳市| 南阳市| 佛坪县| 色达县| 柘荣县| 托里县| 鄂伦春自治旗| 鹤壁市| 普兰县| 廉江市| 大竹县| 东乌| 福安市| 南昌市|