关于Dapper.NET的相关论述
?
年少時,為何不為自己的夢想去拼搏一次呢?縱使頭破血流,也不悔有那年少輕狂。感慨很多,最近事情也很多,博客也很少更新了,畢竟每個人都需要為自己的生活去努力。
? ?最近在一個群里遇到一個人說的話,在這里不再贅述,大概意思就是自己各種精通各種懂,面試時各種裝逼各種吊,本人真誠的求教了一下他,問他是否懂這些東西的底層原理,是否了解過底層源碼,能否根據實際情況修改源碼,誰知被他吐槽說裝逼,說知識那么多不能什么都看源碼和理解原理吧。但是我只想說,這可是你自己說自己精通,難道精通的框架不該了解源碼和原理嗎?難道精通就是只知道怎么簡單的應用嗎?難道是我聊天的方式不對?
? ?最近遇到一個問題,那就是有關Dapper.NET的一些問題,Dapper.NET的效率為何很高?該組件的運行原理是什么?說句實話,我找了很久都沒有發現類似的文章,不知道是不是我的搜素方式不對,還希望發現類似好的文章的朋友發給我看看,知識在于分享嘛,不要吝嗇你的知識,讓我們一起進步吧。
? ?在這里簡單介紹一下其原理 ?
一.Dapper.NET概述:
? 項目開發時,我們都是需要考慮項目的技術架構,尤其是對數據庫底層的考慮比較多。現在對于數據庫的訪問有ADO.NET,EF,Dapper.NET等等,不同的情況會有不同的選擇,討論的時候都會說到“xx很牛逼,xx效率很高”等等,總之需要干一場,才算我們開過會。(很多時候,在開會前項目選什么技術早就定了,但是不開個會就顯得做事不嚴謹...),在選用Dapper.NET時,有人說到Dapper.NET效率高,很牛逼,也不知道那個新人說了一句“為什么Dapper.NET效率高?”
? ?好尷尬...
? ?Dapper.NET是一個簡單的ORM,專門從SQL查詢結果中快速生成對象。Dapper.Net支持執行sql查詢并將其結果映射到強類型列表或動態對象列表。Dapper.Net緩存每個查詢的信息。這種全面的緩存有助于從大約兩倍于LINQ到SQL的查詢生成對象。當前緩存由兩個ConcurrentDictionary對象處理,它們從不被清除。
? ?Dapper.Net通過擴展方法將兩個映射函數添加到IDbConnection接口,這兩個函數都命名為ExecuteMapperQuery。第一個映射結果是一個強類型列表,而第二個映射結果是一個動態對象列表。ExecuteMapperCommand執行并且不返回結果集。所有三個方法都將參數接受為匿名類,其中屬性值映射到同名的SQL參數。
? ?Dapper.Net旨在僅處理結果集到對象映射。它不處理對象之間的關系,它不會自動生成任何類型的SQL查詢。
二.Dapper.NET原理淺析:
? ?通過Dapper.NET的源碼我們可以發現其主要是“分部方法和分部類”,有關于“分部方法和分部類”的知識可以看這篇博客:http://www.cnblogs.com/pengze0902/p/6369541.html。Dapper.Net也假定連接已打開并準備就緒,Dapper.NET通過對IDbConnection接口進行擴展。在Dapper.NET對數據庫連接完成后,可以進行相關的操作,接下來我們就來看一下這些操作的實現方式。
? ?1.Query()方法:
Query<T>(this IDbConnection cnn, string sql, object param = null,IDbTransaction transaction = null, bool buffered = true,
int? commandTimeout = null, CommandType? commandType = null
? ?改方法表示執行查詢,返回按T輸入的數據。該方法是Query()方法的泛型方法,有7個參數,第一個參數為IDbConnection擴展類,表示對IDbConnection接口進行擴展,該方法使用了可選參數,提高方法的擴展性。在Query方法的實現中,有一個CommandDefinition類,用來表示sql操作的關鍵方面。在該類下有一個GetInit()方法。
? ?2.GetInit()方法:
? ? 我們都知道Dapper.NET通過Emit反射IDataReader的序列隊列,來快速的得到和產生對象。GetInit()方法是一個靜態方法,該方法的“Type commandType”參數表示連接關聯的Command對象,返回一個Action<IDbCommand>委托。
? ?我們就具體看一下是如何通過Emit反射IDataReader的序列隊列的。
if (SqlMapper.Link<Type, Action<IDbCommand>>.TryGet(commandInitCache, commandType, out action)){ return action; }
? ?Link<TKey, TValue>是一個泛型分部類,這是一個微緩存,查看是否存在一個Action<IDbCommand>的委托。
var bindByName = GetBasicPropertySetter(commandType, "BindByName", typeof(bool));var initialLongFetchSize = GetBasicPropertySetter(commandType, "InitialLONGFetchSize",
typeof(int));
? ?以上兩個操作主要獲取BindByName和InitialLONGFetchSize的獲取基本屬性設置。
if (bindByName != null || initialLongFetchSize != null)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? var method = new DynamicMethod(commandType.Name + "_init", null, new Type[] { typeof(IDbCommand) });
? ? ? ? ? ? ? ? var il = method.GetILGenerator();
? ? ? ? ? ? ? ? if (bindByName != null)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? il.Emit(OpCodes.Ldarg_0);
? ? ? ? ? ? ? ? ? ? il.Emit(OpCodes.Castclass, commandType);
? ? ? ? ? ? ? ? ? ? il.Emit(OpCodes.Ldc_I4_1);
? ? ? ? ? ? ? ? ? ? il.EmitCall(OpCodes.Callvirt, bindByName, null);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? if (initialLongFetchSize != null)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? il.Emit(OpCodes.Ldarg_0);
? ? ? ? ? ? ? ? ? ? il.Emit(OpCodes.Castclass, commandType);
? ? ? ? ? ? ? ? ? ? il.Emit(OpCodes.Ldc_I4_M1);
? ? ? ? ? ? ? ? ? ? il.EmitCall(OpCodes.Callvirt, initialLongFetchSize, null);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? il.Emit(OpCodes.Ret);
? ? ? ? ? ? ? ? action = (Action<IDbCommand>)method.CreateDelegate(typeof(Action<IDbCommand>));
? ? ? ? ? ? }
? ?這一步是該操作的核心部分,利用Emit反射操作。根據上一步獲取的對應名稱的基本屬性設置,采用DynamicMethod對象,定義和表示一個可以編譯,執行和丟棄的動態方法。丟棄的方法可用于垃圾回收。調用該對象的GetILGenerator方法,返回方法的Microsoft中間語言(MSIL)生成器,默認的MSIL流大小為64字節。判斷基本屬性設置不為空后,調用ILGenerator類的Emit方法,Emit()將指定的指令放在指令流上,該方法接收一個IL流。EmitCall()將?call?或?callvirt?指令置于 Microsoft 中間語言 (MSIL) 流,以調用varargs?方法。我們看到OpCodes類,該類描述中間語言 (IL) 指令。CreateDelegate()完成動態方法并創建一個可用于執行它的委托。
? ?通過以上的反射操作構建好對象后,就會接著執行對應的數據庫操作。
?3.QueryImpl():
private static IEnumerable<T> QueryImpl<T>(this IDbConnection cnn, CommandDefinition command, Type effectiveType)
? ? ? ? {
? ? ? ? ? ? object param = command.Parameters;
? ? ? ? ? ? var identity = new Identity(command.CommandText, command.CommandType, cnn, effectiveType, param == null ? null : param.GetType(), null);
? ? ? ? ? ? var info = GetCacheInfo(identity, param, command.AddToCache);
? ? ? ? ? ? IDbCommand cmd = null;
? ? ? ? ? ? IDataReader reader = null;
? ? ? ? ? ? bool wasClosed = cnn.State == ConnectionState.Closed;
? ? ? ? ? ? try
? ? ? ? ? ? {
? ? ? ? ? ? ? ? cmd = command.SetupCommand(cnn, info.ParamReader);
? ? ? ? ? ? ? ? if (wasClosed) cnn.Open();
? ? ? ? ? ? ? ? reader = cmd.ExecuteReader(wasClosed ? CommandBehavior.CloseConnection | CommandBehavior.SequentialAccess : CommandBehavior.SequentialAccess);
? ? ? ? ? ? ? ? wasClosed = false;?
? ? ? ? ? ? ? ? var tuple = info.Deserializer;
? ? ? ? ? ? ? ? int hash = GetColumnHash(reader);
? ? ? ? ? ? ? ? if (tuple.Func == null || tuple.Hash != hash)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? if (reader.FieldCount == 0)?
? ? ? ? ? ? ? ? ? ? ? ? yield break;
? ? ? ? ? ? ? ? ? ? tuple = info.Deserializer = new DeserializerState(hash, GetDeserializer(effectiveType, reader, 0, -1, false));
? ? ? ? ? ? ? ? ? ? if (command.AddToCache) SetQueryCache(identity, info);
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? var func = tuple.Func;
? ? ? ? ? ? ? ? var convertToType = Nullable.GetUnderlyingType(effectiveType) ?? effectiveType;
? ? ? ? ? ? ? ? while (reader.Read())
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? object val = func(reader);
? ? ? ? ? ? ? ? ? ? if (val == null || val is T)
? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? yield return (T)val;
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? else
? ? ? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? ? ? yield return (T)Convert.ChangeType(val, convertToType, CultureInfo.InvariantCulture);
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? while (reader.NextResult()) { }
? ? ? ? ? ? ? ? reader.Dispose();
? ? ? ? ? ? ? ? reader = null;
? ? ? ? ? ? ? ? command.OnCompleted();
? ? ? ? ? ? }
? ? ? ? ? ? finally
? ? ? ? ? ? {
? ? ? ? ? ? ? ? if (reader != null)
? ? ? ? ? ? ? ? {
? ? ? ? ? ? ? ? ? ? if (!reader.IsClosed) try { cmd.Cancel(); }
? ? ? ? ? ? ? ? ? ? ? ? catch { /* don't spoil the existing exception */ }
? ? ? ? ? ? ? ? ? ? reader.Dispose();
? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? if (wasClosed) cnn.Close();
? ? ? ? ? ? ? ? if (cmd != null) cmd.Dispose();
? ? ? ? ? ? }
? ? ? ? }
? ? 該方法為執行查詢操作的核心方法,通過CommandDefinition類的相關操作后,獲取到相應的對象后,執行這一步操作。該方法是IDbConnection的擴展方法,CommandDefinition表示sql的相關操作對象,Type表示傳入的一個有效的類型。Identity對象表示Dapper中的緩存查詢的標識,該類是一個分部類,可以對其進行相應的擴展。GetCacheInfo()獲取緩存信息。
三.Dapper.NET擴展:
? ?這一部分是借花獻佛,該部分代碼是對Dapper.NET代碼做一封裝,可以類似于操作其他ORM的方式,需要者可以自取,就不要到處去找這些東西了。
? ? Dapper.NET擴展方法包
? ? Dapper包
四.總結:
? ? 這篇博文是我硬著頭皮寫的,因為基本沒有類似的文章,連參考的資料都沒有,最多的就是調用代碼的demo,對于原理和底層源碼解析基本沒有,在這里就用這篇博文引出大神對其全面的解析。希望對大家有一點幫助,也算是盡力了。
原文地址:http://www.cnblogs.com/pengze0902/p/6523458.html
.NET社區新聞,深度好文,微信中搜索dotNET跨平臺或掃描二維碼關注
贊賞
總結
以上是生活随笔為你收集整理的关于Dapper.NET的相关论述的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ASP.NET Core 优雅的在开发环
- 下一篇: 发布 ASP.NET Core 应用