您好,登錄后才能下訂單哦!
在開發(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程序包”,如下圖所示:
然后在彈出的窗口中搜索“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),如下圖:
就是首先定義了一系列的借口,如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
免責(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)容。