年少時,為何不為自己的夢想去拼搏一次呢嚎尤?縱使頭破血流荔仁,也不悔有那年少輕狂。感慨很多,最近事情也很多乏梁,博客也很少更新了次洼,畢竟每個人都需要為自己的生活去努力。
最近在一個群里遇到一個人說的話遇骑,在這里不再贅述卖毁,大概意思就是自己各種精通各種懂,面試時各種裝逼各種吊落萎,本人真誠的求教了一下他亥啦,問他是否懂這些東西的底層原理,是否了解過底層源碼练链,能否根據(jù)實際情況修改源碼翔脱,誰知被他吐槽說裝逼,說知識那么多不能什么都看源碼和理解原理吧媒鼓。但是我只想說届吁,這可是你自己說自己精通,難道精通的框架不該了解源碼和原理嗎绿鸣?難道精通就是只知道怎么簡單的應(yīng)用嗎疚沐?難道是我聊天的方式不對?
最近遇到一個問題潮模,那就是有關(guān)Dapper.NET的一些問題濒旦,Dapper.NET的效率為何很高?該組件的運行原理是什么再登?說句實話尔邓,我找了很久都沒有發(fā)現(xiàn)類似的文章,不知道是不是我的搜素方式不對锉矢,還希望發(fā)現(xiàn)類似好的文章的朋友發(fā)給我看看梯嗽,知識在于分享嘛,不要吝嗇你的知識沽损,讓我們一起進步吧灯节。
在這里簡單介紹一下其原理
一.Dapper.NET概述:
項目開發(fā)時,我們都是需要考慮項目的技術(shù)架構(gòu)绵估,尤其是對數(shù)據(jù)庫底層的考慮比較多⊙捉現(xiàn)在對于數(shù)據(jù)庫的訪問有ADO.NET,EF国裳,Dapper.NET等等形入,不同的情況會有不同的選擇,討論的時候都會說到“xx很牛逼缝左,xx效率很高”等等亿遂,總之需要干一場浓若,才算我們開過會。(很多時候蛇数,在開會前項目選什么技術(shù)早就定了挪钓,但是不開個會就顯得做事不嚴(yán)謹(jǐn)...),在選用Dapper.NET時耳舅,有人說到Dapper.NET效率高碌上,很牛逼,也不知道那個新人說了一句“為什么Dapper.NET效率高浦徊?”
好尷尬...
Dapper.NET是一個簡單的ORM馏予,專門從SQL查詢結(jié)果中快速生成對象。Dapper.Net支持執(zhí)行sql查詢并將其結(jié)果映射到強類型列表或動態(tài)對象列表辑畦。Dapper.Net緩存每個查詢的信息吗蚌。這種全面的緩存有助于從大約兩倍于LINQ到SQL的查詢生成對象。當(dāng)前緩存由兩個ConcurrentDictionary對象處理纯出,它們從不被清除宣旱。
Dapper.Net通過擴展方法將兩個映射函數(shù)添加到IDbConnection接口工育,這兩個函數(shù)都命名為ExecuteMapperQuery。第一個映射結(jié)果是一個強類型列表,而第二個映射結(jié)果是一個動態(tài)對象列表竭鞍。ExecuteMapperCommand執(zhí)行并且不返回結(jié)果集编振。所有三個方法都將參數(shù)接受為匿名類宜狐,其中屬性值映射到同名的SQL參數(shù)肛搬。
Dapper.Net旨在僅處理結(jié)果集到對象映射。它不處理對象之間的關(guān)系鸵赖,它不會自動生成任何類型的SQL查詢务漩。
二.Dapper.NET原理淺析:
通過Dapper.NET的源碼我們可以發(fā)現(xiàn)其主要是“分部方法和分部類”,有關(guān)于“分部方法和分部類”的知識可以看這篇博客:http://www.cnblogs.com/pengze0902/p/6369541.html它褪。Dapper.Net也假定連接已打開并準(zhǔn)備就緒饵骨,Dapper.NET通過對IDbConnection接口進行擴展。在Dapper.NET對數(shù)據(jù)庫連接完成后茫打,可以進行相關(guān)的操作居触,接下來我們就來看一下這些操作的實現(xiàn)方式。
1.Query()方法:
Query(thisIDbConnection cnn,stringsql,objectparam =null,
IDbTransaction transaction =null,boolbuffered =true,int? commandTimeout =null, CommandType? commandType =null
改方法表示執(zhí)行查詢老赤,返回按T輸入的數(shù)據(jù)轮洋。該方法是Query()方法的泛型方法,有7個參數(shù)抬旺,第一個參數(shù)為IDbConnection擴展類弊予,表示對IDbConnection接口進行擴展,該方法使用了可選參數(shù)嚷狞,提高方法的擴展性块促。在Query方法的實現(xiàn)中荣堰,有一個CommandDefinition類床未,用來表示sql操作的關(guān)鍵方面竭翠。在該類下有一個GetInit()方法。
2.GetInit()方法:
我們都知道Dapper.NET通過Emit反射IDataReader的序列隊列薇搁,來快速的得到和產(chǎn)生對象斋扰。GetInit()方法是一個靜態(tài)方法,該方法的“Type commandType”參數(shù)表示連接關(guān)聯(lián)的Command對象啃洋,返回一個Action委托传货。
我們就具體看一下是如何通過Emit反射IDataReader的序列隊列的。
if(SqlMapper.Link>.TryGet(commandInitCache, commandType,outaction)){returnaction; }
Link是一個泛型分部類宏娄,這是一個微緩存问裕,查看是否存在一個Action的委托。
varbindByName = GetBasicPropertySetter(commandType,"BindByName",typeof(bool));varinitialLongFetchSize = GetBasicPropertySetter(commandType,"InitialLONGFetchSize",typeof(int));
以上兩個操作主要獲取BindByName和InitialLONGFetchSize的獲取基本屬性設(shè)置孵坚。
if(bindByName !=null|| initialLongFetchSize !=null)
{varmethod =newDynamicMethod(commandType.Name +"_init",null,newType[] {typeof(IDbCommand) });varil =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反射操作。根據(jù)上一步獲取的對應(yīng)名稱的基本屬性設(shè)置卖宠,采用DynamicMethod對象巍杈,定義和表示一個可以編譯,執(zhí)行和丟棄的動態(tài)方法扛伍。丟棄的方法可用于垃圾回收筷畦。調(diào)用該對象的GetILGenerator方法,返回方法的Microsoft中間語言(MSIL)生成器刺洒,默認(rèn)的MSIL流大小為64字節(jié)鳖宾。判斷基本屬性設(shè)置不為空后,調(diào)用ILGenerator類的Emit方法逆航,Emit()將指定的指令放在指令流上鼎文,該方法接收一個IL流。EmitCall()將
call
或
callvirt
指令置于 Microsoft 中間語言 (MSIL) 流纸泡,以調(diào)用
varargs
方法漂问。我們看到OpCodes類,該類描述中間語言 (IL) 指令女揭。CreateDelegate()完成動態(tài)方法并創(chuàng)建一個可用于執(zhí)行它的委托蚤假。
通過以上的反射操作構(gòu)建好對象后,就會接著執(zhí)行對應(yīng)的數(shù)據(jù)庫操作吧兔。
3.QueryImpl():
privatestaticIEnumerable
QueryImpl(thisIDbConnection cnn, CommandDefinition command, Type effectiveType)
{objectparam =command.Parameters;varidentity =newIdentity(command.CommandText, command.CommandType, cnn, effectiveType, param ==null?null: param.GetType(),null);varinfo =GetCacheInfo(identity, param, command.AddToCache);
IDbCommand cmd=null;
IDataReader reader=null;boolwasClosed = 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;vartuple =info.Deserializer;inthash =GetColumnHash(reader);if(tuple.Func ==null|| tuple.Hash !=hash)
{if(reader.FieldCount ==0)yieldbreak;
tuple= info.Deserializer =newDeserializerState(hash, GetDeserializer(effectiveType, reader,0, -1,false));if(command.AddToCache) SetQueryCache(identity, info);
}varfunc =tuple.Func;varconvertToType = Nullable.GetUnderlyingType(effectiveType) ??effectiveType;while(reader.Read())
{objectval =func(reader);if(val ==null|| valisT)
{yieldreturn(T)val;
}else{yieldreturn(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();
}
}
該方法為執(zhí)行查詢操作的核心方法磷仰,通過CommandDefinition類的相關(guān)操作后,獲取到相應(yīng)的對象后境蔼,執(zhí)行這一步操作灶平。該方法是IDbConnection的擴展方法伺通,CommandDefinition表示sql的相關(guān)操作對象,Type表示傳入的一個有效的類型逢享。Identity對象表示Dapper中的緩存查詢的標(biāo)識罐监,該類是一個分部類,可以對其進行相應(yīng)的擴展瞒爬。GetCacheInfo()獲取緩存信息弓柱。
三.Dapper.NET擴展:
這一部分是借花獻佛,該部分代碼是對Dapper.NET代碼做一封裝侧但,可以類似于操作其他ORM的方式矢空,需要者可以自取,就不要到處去找這些東西了禀横。
四.總結(jié):
這篇博文是我硬著頭皮寫的屁药,因為基本沒有類似的文章,連參考的資料都沒有柏锄,最多的就是調(diào)用代碼的demo酿箭,對于原理和底層源碼解析基本沒有,在這里就用這篇博文引出大神對其全面的解析绢彤。希望對大家有一點幫助七问,也算是盡力了。