關於Dapper.NET的相關論述

   年少時,爲什麼不爲本身的夢想去拼搏一次呢?縱使頭破血流,也不悔有那年少輕狂。感慨不少,最近事情也不少,博客也不多更新了,畢竟每一個人都須要爲本身的生活去努力。html

   最近在一個羣裏遇到一我的說的話,在這裏再也不贅述,大概意思就是本身各類精通各類懂,面試時各類裝逼各類吊,本人真誠的求教了一下他,問他是否懂這些東西的底層原理,是否瞭解過底層源碼,可否根據實際狀況修改源碼,誰知被他吐槽說裝逼,說知識那麼多不能什麼都看源碼和理解原理吧。可是我只想說,這但是你本身說本身精通,難道精通的框架不應瞭解源碼和原理嗎?難道精通就是隻知道怎麼簡單的應用嗎?難道是我聊天的方式不對?git

   最近遇到一個問題,那就是有關Dapper.NET的一些問題,Dapper.NET的效率爲什麼很高?該組件的運行原理是什麼?說句實話,我找了好久都沒有發現相似的文章,不知道是否是個人搜素方式不對,還但願發現相似好的文章的朋友發給我看看,知識在於分享嘛,不要吝嗇你的知識,讓咱們一塊兒進步吧。github

   在這裏簡單介紹一下其原理  面試

一.Dapper.NET概述:

  項目開發時,咱們都是須要考慮項目的技術架構,尤爲是對數據庫底層的考慮比較多。如今對於數據庫的訪問有ADO.NET,EF,Dapper.NET等等,不一樣的狀況會有不一樣的選擇,討論的時候都會說到「xx很牛逼,xx效率很高」等等,總之須要幹一場,纔算咱們開過會。(不少時候,在開會前項目選什麼技術早就定了,可是不開個會就顯得作事不嚴謹...),在選用Dapper.NET時,有人說到Dapper.NET效率高,很牛逼,也不知道那個新人說了一句「爲何Dapper.NET效率高?」sql

   好尷尬...數據庫

   Dapper.NET是一個簡單的ORM,專門從SQL查詢結果中快速生成對象。Dapper.Net支持執行sql查詢並將其結果映射到強類型列表或動態對象列表。Dapper.Net緩存每一個查詢的信息。這種全面的緩存有助於從大約兩倍於LINQ到SQL的查詢生成對象。當前緩存由兩個ConcurrentDictionary對象處理它們從不被清除。緩存

   Dapper.Net經過擴展方法將兩個映射函數添加到IDbConnection接口,這兩個函數都命名爲ExecuteMapperQuery第一個映射結果是一個強類型列表,而第二個映射結果是一個動態對象列表。ExecuteMapperCommand執行而且不返回結果集。全部三個方法都將參數接受爲匿名類,其中屬性值映射到同名的SQL參數。架構

   Dapper.Net旨在僅處理結果集到對象映射。它不處理對象之間的關係,它不會自動生成任何類型的SQL查詢。app

二.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,對於原理和底層源碼解析基本沒有,在這裏就用這篇博文引出大神對其全面的解析。但願對你們有一點幫助,也算是盡力了。

相關文章
相關標籤/搜索