年少時, 為何不為自己的夢想去拼搏一次呢?縱使頭破血流, 也不悔有那年少輕狂。 感慨很多, 最近事情也很多, 博客也很少更新了, 畢竟每個人都需要為自己的生活去努力。
最近在一個群裡遇到一個人說的話, 在這裡不再贅述, 大概意思就是自己各種精通各種懂, 面試時各種裝逼各種吊, 本人真誠的求教了一下他, 問他是否懂這些東西的底層原理, 是否瞭解過底層源碼, 能否根據實際情況修改源碼, 誰知被他吐槽說裝逼, 說知識那麼多不能什麼都看源碼和理解原理吧。 但是我只想說, 這可是你自己說自己精通,
最近遇到一個問題, 那就是有關Dapper.NET的一些問題, Dapper.NET的效率為何很高?該組件的運行原理是什麼?說句實話, 我找了很久都沒有發現類似的文章, 不知道是不是我的搜素方式不對, 還希望發現類似好的文章的朋友發給我看看, 知識在於分享嘛, 不要吝嗇你的知識, 讓我們一起進步吧。
在這裡簡單介紹一下其原理
一.Dapper.NET概述:
專案開發時, 我們都是需要考慮專案的技術架構, 尤其是對資料庫底層的考慮比較多。 現在對於資料庫的訪問有ADO.NET, EF, Dapper.NET等等, 不同的情況會有不同的選擇, 討論的時候都會說到“xx很牛逼,
好尷尬...
Dapper.NET是一個簡單的ORM, 專門從SQL查詢結果中快速生成對象。 Dapper.Net支持執行sql查詢並將其結果映射到強類型清單或動態物件清單。 Dapper.Net緩存每個查詢的資訊。 這種全面的緩存有助於從大約兩倍於LINQ到SQL的查詢生成物件。 當前緩存由兩個ConcurrentDictionary物件處理, 它們從不被清除。
Dapper.Net通過擴展方法將兩個映射函數添加到IDbConnection介面, 這兩個函數都命名為ExecuteMapperQuery。 第一個映射結果是一個強類型列表, 而第二個映射結果是一個動態物件清單。 ExecuteMapperCommand執行並且不返回結果集。
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(this IDbConnection cnn, string sql, object param = null,改方法表示執行查詢, 返回按T輸入的資料。 該方法是Query方法的泛型方法, 有7個參數, 第一個參數為IDbConnection擴展類, 表示對IDbConnection介面進行擴展, 該方法使用了可選參數,
2.GetInit方法:
我們都知道Dapper.NET通過Emit反射IDataReader的序列佇列, 來快速的得到和產生物件。 GetInit方法是一個靜態方法, 該方法的“Type commandType”參數表示連接關聯的Command物件, 返回一個Action委託。
我們就具體看一下是如何通過Emit反射IDataReader的序列佇列的。
if (SqlMapper.LinkLink是一個泛型分佈類, 這是一個微緩存, 查看是否存在一個Action的委託。
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)method.CreateDelegate(typeof(Action)); }這一步是該操作的核心部分, 利用Emit反射操作。 根據上一步獲取的對應名稱的基本屬性設置, 採用DynamicMethod物件, 定義和表示一個可以編譯, 執行和丟棄的動態方法。 丟棄的方法可用於垃圾回收。 調用該物件的GetILGenerator方法, 返回方法的Microsoft中間語言(MSIL)生成器,
通過以上的反射操作構建好物件後, 就會接著執行對應的資料庫操作。
3.QueryImpl:
private static IEnumerable QueryImpl(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的方式,需要者可以自取,就不要到處去找這些東西了。
四.總結:
這篇博文是我硬著頭皮寫的,因為基本沒有類似的文章,連參考的資料都沒有,最多的就是調用代碼的demo,對於原理和底層源碼解析基本沒有,在這裡就用這篇博文引出大神對其全面的解析。希望對大家有一點幫助,也算是盡力了。
四.總結:
這篇博文是我硬著頭皮寫的,因為基本沒有類似的文章,連參考的資料都沒有,最多的就是調用代碼的demo,對於原理和底層源碼解析基本沒有,在這裡就用這篇博文引出大神對其全面的解析。希望對大家有一點幫助,也算是盡力了。