浅析ado.net获取数据库元数据信息
寫(xiě)這個(gè)文章源于早先對(duì)ADO.Net獲取數(shù)據(jù)庫(kù)元數(shù)據(jù)上的認(rèn)識(shí),去年我在閱讀ADO.Net Core Reference的時(shí)候曾經(jīng)注意過(guò)DataSet的FillSchema的這個(gè)方法。這方面,在我之前的隨筆中提到過(guò)Typed DataSet,而FillSchem與WriteXmlSchema的結(jié)合使用可以獲得數(shù)據(jù)庫(kù)的表結(jié)構(gòu)架構(gòu),從而使用相應(yīng)工具生成強(qiáng)類(lèi)型的DataSet。但是我記得作者建議在具體應(yīng)用開(kāi)發(fā)中盡量少用FillSchema這個(gè)方法,因?yàn)槌鲇谛阅芸紤],其一般只適合作為測(cè)試過(guò)程中的一個(gè)方法。
當(dāng)時(shí)我的理解就是,這是一個(gè)獲取數(shù)據(jù)庫(kù)元數(shù)據(jù)的一個(gè)方便的方法,但是由于其對(duì)性能的影響,因此通常應(yīng)用中比較少用。而在我后面的開(kāi)發(fā)中也未曾有機(jī)會(huì)接觸這個(gè)方法。
今年早先1月份的時(shí)候看DAAB,注意到其封裝的DataCommand對(duì)象提供了動(dòng)態(tài)獲取存儲(chǔ)過(guò)程信息的支持:DeriveParameters。當(dāng)時(shí)我的第一印象是,這也是獲取數(shù)據(jù)庫(kù)的“元數(shù)據(jù)”,因?yàn)橹坝羞^(guò)FillSchema對(duì)性能影響上的認(rèn)識(shí),我當(dāng)時(shí)就產(chǎn)生了一個(gè)問(wèn)號(hào):這樣做適合嗎?自動(dòng)填充Command對(duì)象的Parameter集合,會(huì)影響應(yīng)用程序的性能嗎?
就此我也請(qǐng)教過(guò)M$的專(zhuān)家,給我的回答是兩者機(jī)制不同,后者對(duì)性能影響不大。
昨日翻倒年初對(duì)這個(gè)問(wèn)題疑惑而提的一篇帖子,突然很想進(jìn)一步找找這兩中方法的區(qū)別之處,簡(jiǎn)單了解了一下,以下做個(gè)簡(jiǎn)單的歸納。
DeriveParameters方法
先說(shuō)簡(jiǎn)單的一個(gè)。DeriveParameters是SqlCommandBuilder類(lèi)的一個(gè)公共方法,提供一個(gè)SqlCommannd的參數(shù),該Command對(duì)象作為獲取到的Parameters的存放容器。其實(shí)SqlCommand本身就有一個(gè)DeriveParameters的方法,但是它是內(nèi)部方法,而SqlCommandBuilder.DeriveParameters就是封裝了該方法的調(diào)用:
1public?static?void?DeriveParameters(SqlCommand?command)2{
3??????SqlConnection.SqlClientPermission.Demand();
4??????if?(command?==?null)
5??????{
6????????????//?throw?an?exception
7??????}
8??????command.DeriveParameters();
9}
來(lái)看一下SqlCommand的DeriveParameters方法:
?1internal?void?DeriveParameters()
?2{
?3??????
?4??????//?Validate?command?type(is?storedprocedure?)?and?command?info
?5??????
?6
?7??????//?Retrieve?command?text?detail
?8??????string[]?txtCommand?=?ADP.ParseProcedureName(this.CommandText);
?9
10??????SqlCommand?cmdDeriveCommand?=?null;
11
12??????this.cmdText?=?"sp_procedure_params_rowset";
13??????if?(txtCommand[1]?!=?null)
14??????{
15????????????this.cmdText?=?"["?+?txtCommand[1]?+?"].."?+?this.cmdText;
16
17????????????if?(txtCommand[0]?!=?null)
18????????????{
19??????????????????this.cmdText?=?txtCommand[0]?+?"."?+?this.cmdText;
20????????????}
21
22????????????cmdDeriveCommand?=?new?SqlCommand(this.cmdText,?this.Connection);
23??????}
24??????else
25??????{
26????????????cmdDeriveCommand?=?new?SqlCommand(this.cmdText,?this.Connection);
27??????}
28??????cmdDeriveCommand.CommandType?=?CommandType.StoredProcedure;
29??????cmdDeriveCommand.Parameters.Add(new?SqlParameter("@procedure_name",?SqlDbType.NVarChar,?0xff));
30??????cmdDeriveCommand.Parameters[0].Value?=?txtCommand[3];
31??????ArrayList?parms?=?new?ArrayList();
32??????try
33??????{
34????????????try
35????????????{
36??????????????????using?(SqlDataReader?drParam?=?cmdDeriveCommand.ExecuteReader())
37??????????????????{
38????????????????????????SqlParameter?parameter?=?null;
39????????????????????????while?(drParam.Read())
40????????????????????????{
41??????????????????????????????parameter?=?new?SqlParameter();
42??????????????????????????????parameter.ParameterName?=?(string)?drParam["PARAMETER_NAME"];
43??????????????????????????????parameter.SqlDbType?=?MetaType.GetSqlDbTypeFromOleDbType((short)?drParam["DATA_TYPE"],?(string)?drParam["TYPE_NAME"]);
44??????????????????????????????object?len?=?drParam["CHARACTER_MAXIMUM_LENGTH"];
45??????????????????????????????if?(len?is?int)
46??????????????????????????????{
47????????????????????????????????????parameter.Size?=?(int)?len;
48??????????????????????????????}
49??????????????????????????????parameter.Direction?=?this.ParameterDirectionFromOleDbDirection((short)?drParam["PARAMETER_TYPE"]);
50??????????????????????????????if?(parameter.SqlDbType?==?SqlDbType.Decimal)
51??????????????????????????????{
52????????????????????????????????????parameter.Scale?=?(byte)?(((short)?drParam["NUMERIC_SCALE"])?&?0xff);
53????????????????????????????????????parameter.Precision?=?(byte)?(((short)?drParam["NUMERIC_PRECISION"])?&?0xff);
54??????????????????????????????}
55??????????????????????????????parms.Add(parameter);
56????????????????????????}
57??????????????????}
58????????????}
59????????????finally
60????????????{
61??????????????????cmdDeriveCommand.Connection?=?null;
62????????????}
63??????}
64??????catch
65??????{
66????????????throw;
67??????}
68
69??????if?(params.Count?==?0)
70??????{
71????????????//?throw?an?exception?that?current?storedprocedure?does?not?exist
72??????}
73??????
74??????this.Parameters.Clear();
75??????foreach?(object?parm?in?parms)
76??????{
77????????????this._parameters.Add(parm);
78??????}
79}
ADP.ParseProcedureName其實(shí)就是獲取存儲(chǔ)過(guò)程命令的細(xì)節(jié)信息,有興趣的可以反編譯來(lái)看看。
縱觀整個(gè)方法,有效性驗(yàn)證-〉獲取命令字符串-〉執(zhí)行查詢-〉填充參數(shù)列表-〉返回。應(yīng)該是非常簡(jiǎn)潔明朗的,最多也就是在數(shù)據(jù)庫(kù)Query的階段需要有一個(gè)來(lái)回,其他操作根本就談不上有什么復(fù)雜度,而且也不存在大數(shù)據(jù)的對(duì)象,對(duì)性能的損耗談不上多巨大。
下面來(lái)看看FillSchema的處理過(guò)程
FillSchema方法
這個(gè)部分因?yàn)榇a比較多,所以我就抽關(guān)鍵的部分來(lái)看一下。
首先,FillSchema是DataAdapter類(lèi)定義的一個(gè)方法,而具體實(shí)現(xiàn)則是在該類(lèi)的子類(lèi)DBDataAdapter中完成的(SqlDataAdapter繼承于DBDataAdapter)。
通過(guò)反編譯,可以發(fā)現(xiàn)FillSchema的關(guān)鍵處理步驟是在其調(diào)用私有方法FillSchemaFromCommand來(lái)完成的。簡(jiǎn)單看一下該方法體的內(nèi)容:
?2{
?3??????IDbConnection?connection?=?DbDataAdapter.GetConnection(command,?"FillSchema");
?4??????ConnectionState?state?=?ConnectionState.Open;
?5??????DataTable[]?arrTables?=?new?DataTable[0];
?6??????try
?7??????{
?8????????????try
?9????????????{
10??????????????????DbDataAdapter.QuietOpen(connection,?out?state);
11??????????????????using?(IDataReader?reader?=?command.ExecuteReader((behavior?|?CommandBehavior.SchemaOnly)?|?CommandBehavior.KeyInfo))
12??????????????????{
13????????????????????????if?(reader?==?null)
14????????????????????????{
15??????????????????????????????return?arrTables;
16????????????????????????}
17????????????????????????int?tblIndex?=?0;
18????????????????????????while?(true)
19????????????????????????{
20??????????????????????????????if?(0?<?reader.FieldCount)
21??????????????????????????????{
22????????????????????????????????????try
23????????????????????????????????????{
24??????????????????????????????????????????string?txtTableName?=?null;
25??????????????????????????????????????????SchemaMapping?mapping?=?new?SchemaMapping(this,?reader,?true);
26??????????????????????????????????????????if?(data?is?DataTable)
27??????????????????????????????????????????{
28????????????????????????????????????????????????mapping.DataTable?=?(DataTable)?data;
29??????????????????????????????????????????}
30??????????????????????????????????????????else
31??????????????????????????????????????????{
32????????????????????????????????????????????????mapping.DataSet?=?(DataSet)?data;
33????????????????????????????????????????????????txtTableName?=?DbDataAdapter.GetSourceTableName(srcTable,?tblIndex);
34??????????????????????????????????????????}
35??????????????????????????????????????????mapping.SetupSchema(schemaType,?txtTableName,?false,?null,?null);
36??????????????????????????????????????????DataTable?currentTable?=?mapping.DataTable;
37??????????????????????????????????????????if?(currentTable?!=?null)
38??????????????????????????????????????????{
39????????????????????????????????????????????????arrTables?=?DbDataAdapter.AddDataTableToArray(arrTables,?currentTable);
40??????????????????????????????????????????}
41????????????????????????????????????}
42????????????????????????????????????finally
43????????????????????????????????????{
44??????????????????????????????????????????tblIndex++;
45????????????????????????????????????}
46??????????????????????????????}
47??????????????????????????????if?(!reader.NextResult())
48??????????????????????????????{
49????????????????????????????????????return?arrTables;
50??????????????????????????????}
51????????????????????????}
52??????????????????}
53????????????}
54????????????finally
55????????????{
56??????????????????DbDataAdapter.QuietClose(connection,?state);
57????????????}
58??????}
59??????catch
60??????{
61????????????throw;
62??????}
63??????return?arrTables;
64}
首先,該操作含有一個(gè)數(shù)據(jù)庫(kù)的Query操作,這里其實(shí)是調(diào)用DBDataAdapter的SelectCommand的對(duì)象,執(zhí)行一次查詢,然后遍歷查詢返回的所有表,每遍歷到一個(gè)表的時(shí)候,通過(guò)該表的信息實(shí)例化一個(gè)SchemaMapping對(duì)象,再有該對(duì)象創(chuàng)建為DataSet/DataTable創(chuàng)建架構(gòu)信息。
這里,DataSet/DataTable是作為參數(shù)提供的,整個(gè)處理過(guò)程,首先必然的需要完成一次查詢操作,由于使用IDataReader,所以在查詢之后的所有操作期間,連接是保持著的,這一定程度上占用了一些資源(也可以說(shuō)這些資源還不算太昂貴);其次,實(shí)例化一個(gè)SchemaMapping對(duì)象(該對(duì)象是內(nèi)部類(lèi),我在MSDN上沒(méi)有查到相關(guān)介紹性資料),我簡(jiǎn)單看了一下這個(gè)類(lèi)的代碼,在我看來(lái),它的處理過(guò)程應(yīng)該是占據(jù)了整個(gè)過(guò)程蠻大一部分資源的,這方面屬于個(gè)人見(jiàn)解。
由于我的認(rèn)識(shí)上的有限,也為了保證文章的內(nèi)容無(wú)誤導(dǎo),暫且說(shuō)到這里。這個(gè)方法的進(jìn)一步討論希望留給有興趣的朋友。
總結(jié)
以上是我對(duì)這兩個(gè)方法認(rèn)識(shí)方面簡(jiǎn)單的一個(gè)概括,其實(shí)從上面的描述,也打消了我原先認(rèn)為的這兩個(gè)方法在獲取元數(shù)據(jù)上有本質(zhì)的差別。個(gè)人認(rèn)為,之所以獲取結(jié)構(gòu)性元數(shù)據(jù)的消耗大,是因?yàn)楂@取邏輯的繁瑣以及使用的對(duì)象的龐大,而參數(shù)信息相對(duì)而言完全屬于輕量級(jí)的東西,所以所謂性能上的差異并非因?yàn)楂@取機(jī)制的本質(zhì)差異引起的。
?總結(jié)
以上是生活随笔為你收集整理的浅析ado.net获取数据库元数据信息的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 瓷牙多少钱一颗啊?
- 下一篇: 从前有个人爱你很久是什么歌啊?