溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點(diǎn)擊 登錄注冊 即表示同意《億速云用戶服務(wù)條款》

在.NET數(shù)據(jù)庫訪問方面的Dapper類庫介紹

發(fā)布時(shí)間:2020-07-05 13:40:39 來源:網(wǎng)絡(luò) 閱讀:27080 作者:周金橋 欄目:編程語言

在開發(fā)帶數(shù)據(jù)庫的.NET系統(tǒng)中我使用過各種方式,包括直接使用ADO.NET、使用基于ADO.NET封裝的各類工具(其中有自己封裝的)、還有各類ORM類庫,比如NHibernate、MyBatisNet、Linq to SQL、Entity Framwrok等,在上面的工具或類庫中,MyBatisNet一段時(shí)間曾是我的最愛:由于它基于XML的配置可以靈活適應(yīng)一些特殊的場景,不過有時(shí)候在面對中小型項(xiàng)目時(shí)又覺得MyBatisNet有些大材小用,此外還有一個(gè)原因是MyBatisNet這個(gè)基于Java的MyBatis改造而來的項(xiàng)目最近幾乎沒有更新了。
很早就聽說過Dapper這個(gè)類庫了,只不過一直沒有嘗試使用,但是很早就知道它是國外大型IT問答社區(qū)StackOverFlow最早開發(fā)并開源的。最近用了一下,感覺確實(shí)很方便。Dapper的源代碼放在github上托管,并且可以用NuGet方式添加到項(xiàng)目中,只不過我現(xiàn)在開發(fā)的桌面軟件有一部分用戶還在使用WindowsXP系統(tǒng),因此不能使用高于.NET Framrwork4.5以上版本開發(fā)且開發(fā)工具是Visual Studio 2015,這也限制了我不能使用最新版本的Dapper,于是我選擇了Dapper 1.50.2這個(gè)版本。
我們可以在Visual Studio 2015中直接使用NuGet來添加,具體辦法就是“工具”-“NuGet包管理器”-“管理解決方案的BuGet程序包”,如下圖所示:
在.NET數(shù)據(jù)庫訪問方面的Dapper類庫介紹
然后在彈出的窗口中搜索“Dapper”,如下圖所示:
在.NET數(shù)據(jù)庫訪問方面的Dapper類庫介紹
在上述界面中可以選擇安裝到當(dāng)前解決方案的那些項(xiàng)目中,并且還可以指定Dapper的版本。

本文的描述都是針對Dapper 1.50.2版本。

擴(kuò)展方法介紹
在介紹Dapper之前首先要介紹一下.NE中的擴(kuò)展方法。擴(kuò)展方法是.NET3.5添加的一個(gè)特性,使用擴(kuò)展方法可以讓你為現(xiàn)有的類擴(kuò)展方法而無需創(chuàng)建新的派生類。下面以一個(gè)例子來說明:
在我解析某個(gè)XML文件節(jié)點(diǎn)時(shí),需要讀取某個(gè)節(jié)點(diǎn)的屬性,但是這個(gè)節(jié)點(diǎn)并不是一直有這個(gè)屬性值,如下:
<SubNetwork name="SF-SubNetWork" type="">
為了避免name屬性不存在時(shí)拋出異常,我必須先進(jìn)行判斷,如下:

string name=string.Empty;
if (subNetworkNode.Attributes["name"] != null)
{
    name=subNetworkNode.Attributes["name"].Value;
}

如果一個(gè)XML節(jié)點(diǎn)里有幾個(gè)可能不存在的屬性時(shí),就需要處處這樣判斷了,于是我對代碼進(jìn)行了改進(jìn),針對此類情況定義了擴(kuò)展方法,方法如下:

public static class ExtendMethodClass
{
    /// <summary>
    /// 獲取指定屬性的值,如果沒有設(shè)置指定屬性名,則返回空字符串
    /// </summary>
    /// <param name="attributes">XML節(jié)點(diǎn)的屬性集合</param>
    /// <param name="attributeName">屬性名</param>
    /// <returns></returns>
    public static string GetAttributeValue(this XmlAttributeCollection attributes,string attributeName)
    {
        if (string.IsNullOrEmpty(attributeName))
        {
            throw new ArgumentNullException("attributeName", "不能為空");
        }

        if (attributes == null||attributes[attributeName]==null)
        {
            return string.Empty;
        }

        return attributes[attributeName].Value;
    }
}

這樣一來,原來的代碼就可以寫成如下了:

string name = subNetworkNode.Attributes.GetAttributeValue("name");

初一看,就像是XmlAttributeCollection這類原來就有GetAttributeValue(string attributeName)這樣一個(gè)方法,其實(shí)這個(gè)方式是我們自己擴(kuò)展的。
定義擴(kuò)展方法有幾點(diǎn):
1、定義擴(kuò)展方法的類必須用static修飾,即必須為靜態(tài)類。
2、定義的擴(kuò)展方法必須用static修飾,即必須為靜態(tài)方法,同時(shí)方法的第一個(gè)參數(shù)前必須加this修飾,this后必須是類名,表示為this后的類添加擴(kuò)展方法,如本例中this XmlAttributeCollection attributes表示為XmlAttributeCollection這個(gè)類添加擴(kuò)展方法,如果需要在方法體內(nèi)訪問XmlAttributeCollection這個(gè)類的實(shí)例,通過后面的attributes參數(shù)即可(注意這個(gè)參數(shù)的名稱可以隨便?。?/p>

Dapper介紹
通過上面的介紹,大家可以初步了解擴(kuò)展方法是怎么回事。其實(shí)Dapper主要也是用了擴(kuò)展方法為IDbConnection和IDataReader添加擴(kuò)展方法,比如在SqlMapper.cs中有如下代碼為IDbConnection添加擴(kuò)展方法(節(jié)選):

// <summary>
/// Execute parameterized SQL.
/// </summary>
/// <param name="cnn">The connection to query on.</param>
/// <param name="sql">The SQL to execute for this query.</param>
/// <param name="param">The parameters to use for this query.</param>
/// <param name="transaction">The transaction to use for this query.</param>
/// <param name="commandTimeout">Number of seconds before command execution timeout.</param>
/// <param name="commandType">Is it a stored proc or a batch?</param>
/// <returns>The number of rows affected.</returns>
public static int Execute(this IDbConnection cnn, string sql, object param = null, IDbTransaction transaction = null, int? commandTimeout = null, CommandType? commandType = null)
{
    var command = new CommandDefinition(sql, param, transaction, commandTimeout, commandType, CommandFlags.Buffered);
    return ExecuteImpl(cnn, ref command);
}

/// <summary>
/// Execute parameterized SQL.
/// </summary>
/// <param name="cnn">The connection to execute on.</param>
/// <param name="command">The command to execute on this connection.</param>
/// <returns>The number of rows affected.</returns>
public static int Execute(this IDbConnection cnn, CommandDefinition command) => ExecuteImpl(cnn, ref command);

在SqlMapper.IDataReader.cs為IDataReader添加擴(kuò)展方法的代碼(節(jié)選):

/// <summary>
/// Parses a data reader to a sequence of data of the supplied type. Used for deserializing a reader without a connection, etc.
/// </summary>
/// <typeparam name="T">The type to parse from the <paramref name="reader"/>.</typeparam>
/// <param name="reader">The data reader to parse results from.</param>
public static IEnumerable<T> Parse<T>(this IDataReader reader)
{
    if (reader.Read())
    {
        var deser = GetDeserializer(typeof(T), reader, 0, -1, false);
        do
        {
            yield return (T)deser(reader);
        } while (reader.Read());
    }
}

/// <summary>
/// Parses a data reader to a sequence of data of the supplied type (as object). Used for deserializing a reader without a connection, etc.
/// </summary>
/// <param name="reader">The data reader to parse results from.</param>
/// <param name="type">The type to parse from the <paramref name="reader"/>.</param>
public static IEnumerable<object> Parse(this IDataReader reader, Type type)
{
    if (reader.Read())
    {
        var deser = GetDeserializer(type, reader, 0, -1, false);
        do
        {
            yield return deser(reader);
        } while (reader.Read());
    }
}

在本人2011年7月25日寫的一篇名為《利用ADO.NET的體系架構(gòu)打造通用的數(shù)據(jù)庫訪問通用類》的博客當(dāng)中介紹了ADO.NET的體系架構(gòu),如下圖:
在.NET數(shù)據(jù)庫訪問方面的Dapper類庫介紹
就是首先定義了一系列的借口,如IDbConnection之類的,任何基于數(shù)據(jù)庫訪問只要實(shí)現(xiàn)了接口的定義,就都能在.NET訪問,包括了微軟自己對SQL Server和Access等數(shù)據(jù)庫的實(shí)現(xiàn)以及MySQL和Oracle針對這個(gè)定義的第三方實(shí)現(xiàn)(其實(shí)JDBC也是這個(gè)道理,只不過是基于Java實(shí)現(xiàn)罷了)。因?yàn)榘⊿QL Server/MySQL/Oracle/PostgreSQL/SQLite在內(nèi)的數(shù)據(jù)庫都實(shí)現(xiàn)了IDbConnection的定義,而Dapper又是基于IDbConnection的擴(kuò)展,因此使用Dapper理論上可以訪問任何支持ADO.NET訪問的數(shù)據(jù)庫(前提是需要相關(guān)的數(shù)據(jù)庫驅(qū)動,dll形式)。
在使用Dapper的實(shí)際開發(fā)中,用得較多的還是針對IDbConnection的擴(kuò)展方法,主要有:
int Execute():相當(dāng)于Command.ExecuteNonQuery(),指定增加、刪除、修改SQL語句,返回受影響的行數(shù)。
object ExecuteScalar():相當(dāng)于Command. ExecuteScalar(),返回結(jié)果集第一行第一列,用于聚合函數(shù)等。
T ExecuteScalar<T>():相當(dāng)于Command. ExecuteScalar(),返回結(jié)果集第一行第一列,不過返回的結(jié)果指定了具體類型。
IDataReader ExecuteReader():相當(dāng)于Command. ExecuteReader()。
IEnumerable<dynamic> Query()
IEnumerable<T> Query<T>()
IEnumerable<object> Query()
IEnumerable<dynamic> Query()
dynamic QueryFirst()
dynamic QueryFirstOrDefault()
dynamic QuerySingle()
dynamic QuerySingleOrDefault()
IEnumerable<T> Query<T>()
T QueryFirst<T>()
T QueryFirstOrDefault<T>()
T QuerySingle<T>()
T QuerySingleOrDefault<T>()
IEnumerable<object> Query()
object QueryFirst()
object QueryFirstOrDefault()
object QuerySingle()
object QuerySingleOrDefault()
對于上面各種類型的Query和返回結(jié)果,就是分幾種情況:返回一個(gè)實(shí)現(xiàn)IEnumerable接口的結(jié)果集,返回單個(gè)結(jié)果,返回單個(gè)結(jié)果或在沒有找到匹配結(jié)果下返回默認(rèn)值(引用類型、數(shù)值類型、枚舉、日期等的默認(rèn)值)

基本用法
使用了Dapper之后,在插入或者查詢時(shí)默認(rèn)是按照數(shù)據(jù)庫字段名與類屬性名不區(qū)分大小寫的情況下對應(yīng)。
加入有在SQL Server中有如下表:

CREATE TABLE IF NOT EXISTS tblBay (
    Id                   integer              NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
    Name                 nvarchar(50)         not null,
    Desc                 nvarchar(100)        not null
    )

同時(shí)有如下類定義:

public class Bay
{
    public int ID { get; set; }
    public string Name { get; set; }
    public string Desc { get; set; }
}

那么插入可以這么寫:

string connectionString = ".";//將這里改成你自己的數(shù)據(jù)庫連接字符串
string sqlInsert = "INSERT INTO tblBay(Name,Desc)VALUES(@Name,@Desc)";
SqlConnection connection = new SqlConnection(connectionString);
Bay bay = new Bay { Name = "test", Desc = "desc" };
connection.Execute(sqlInsert, bay);

查詢可以這么寫:

string connectionString = ".";//將這里改成你自己的數(shù)據(jù)庫連接字符串
string sqlQuery = "select * from tblBay where Id=@Id";
int id = 1;
SqlConnection connection = new SqlConnection(connectionString);
IEnumerable<Bay> bayList = connection. QueryFirstOrDefault<Bay>(sqlQuery,new { @Id = id });

字段與屬性不一致情況下關(guān)聯(lián)
但是在某些情況下,比如使用MySQL數(shù)據(jù)庫時(shí)我們可能會在由多個(gè)單詞構(gòu)成的字段名之間以下劃線分割,如”user_id”、”user_name”等,而定義實(shí)體類時(shí)我們又將實(shí)體類的屬性定義為UserId、UserName,那么就需要為他們之間建立關(guān)聯(lián),比較簡單的一種方式就是在select的時(shí)候使用as。
假定在MySQL中存在如下表:

CREATE TABLE IF NOT EXISTS tblperson (
    user_id               integer              NOT NULL PRIMARY KEY AUTOINCREMENT UNIQUE,
    user_name            nvarchar(50)         not null,
    email                 nvarchar(100)        not null
    )

而對應(yīng)的實(shí)體類為:

public class Person
{
    public int UserId { get; set; }
    public string UserName { get; set; }
    public string Email { get; set; }
}

那么插入可以這么寫:

string connectionString = ".";//將這里改成你自己的數(shù)據(jù)庫連接字符串
string sqlInsert = "INSERT INTO tblperson(user_name,email)VALUES(@Name,@Email)";
SqlConnection connection = new SqlConnection(connectionString);
Person person = new Person { UserName = "test", Email = "email@email.com" };
DynamicParameters parameters = = new DynamicParameters();
parameters.Add("@Name", person.UserName);
parameters.Add("@Email", person.Email);
connection.Execute(sqlInsert, parameters);

查詢可以這么寫:

string connectionString = ".";//將這里改成你自己的數(shù)據(jù)庫連接字符串
string sqlQuery = "select user_id as userId,user_name as username,email from tblperson where user_id=@UserId";
int userId = 1;
SqlConnection connection = new SqlConnection(connectionString);
DynamicParameters parameters = = new DynamicParameters();
parameters.Add("@UserId ", userId);
IEnumerable<Person> bayList = connection. QueryFirstOrDefault<Person>(sqlQuery, parameters);

也就是數(shù)據(jù)庫字段名與實(shí)體類屬性名如果忽略大小寫的情況下是一致的,則我們無需單獨(dú)處理它們之間的映射關(guān)系,如果數(shù)據(jù)庫字段名與實(shí)體類屬性在忽略大小寫的情況下仍然不一致,那么我們需要手動處理映射:在INSERT、DELETE、UPDATE時(shí)可以通過DynamicParameters來處理;在SELECT時(shí)可以通過在SQL語句中使用AS來處理。
有關(guān)Dapper的更進(jìn)一步用法可以查看Dapper的用戶手冊或直接查看源代碼。

周金橋
2018/04/22

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI