浅析ado.net获取数据库元数据信息
寫這個文章源于早先對ADO.Net獲取數(shù)據(jù)庫元數(shù)據(jù)上的認識,去年我在閱讀ADO.Net Core Reference的時候曾經(jīng)注意過DataSet的FillSchema的這個方法。這方面,在我之前的隨筆中提到過Typed DataSet,而FillSchem與WriteXmlSchema的結(jié)合使用可以獲得數(shù)據(jù)庫的表結(jié)構(gòu)架構(gòu),從而使用相應工具生成強類型的DataSet。但是我記得作者建議在具體應用開發(fā)中盡量少用FillSchema這個方法,因為出于性能考慮,其一般只適合作為測試過程中的一個方法。
當時我的理解就是,這是一個獲取數(shù)據(jù)庫元數(shù)據(jù)的一個方便的方法,但是由于其對性能的影響,因此通常應用中比較少用。而在我后面的開發(fā)中也未曾有機會接觸這個方法。
今年早先1月份的時候看DAAB,注意到其封裝的DataCommand對象提供了動態(tài)獲取存儲過程信息的支持:DeriveParameters。當時我的第一印象是,這也是獲取數(shù)據(jù)庫的“元數(shù)據(jù)”,因為之前有過FillSchema對性能影響上的認識,我當時就產(chǎn)生了一個問號:這樣做適合嗎?自動填充Command對象的Parameter集合,會影響應用程序的性能嗎?
就此我也請教過M$的專家,給我的回答是兩者機制不同,后者對性能影響不大。
昨日翻倒年初對這個問題疑惑而提的一篇帖子,突然很想進一步找找這兩中方法的區(qū)別之處,簡單了解了一下,以下做個簡單的歸納。
DeriveParameters方法
先說簡單的一個。DeriveParameters是SqlCommandBuilder類的一個公共方法,提供一個SqlCommannd的參數(shù),該Command對象作為獲取到的Parameters的存放容器。其實SqlCommand本身就有一個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}
來看一下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其實就是獲取存儲過程命令的細節(jié)信息,有興趣的可以反編譯來看看。
縱觀整個方法,有效性驗證-〉獲取命令字符串-〉執(zhí)行查詢-〉填充參數(shù)列表-〉返回。應該是非常簡潔明朗的,最多也就是在數(shù)據(jù)庫Query的階段需要有一個來回,其他操作根本就談不上有什么復雜度,而且也不存在大數(shù)據(jù)的對象,對性能的損耗談不上多巨大。
下面來看看FillSchema的處理過程
FillSchema方法
這個部分因為代碼比較多,所以我就抽關(guān)鍵的部分來看一下。
首先,FillSchema是DataAdapter類定義的一個方法,而具體實現(xiàn)則是在該類的子類DBDataAdapter中完成的(SqlDataAdapter繼承于DBDataAdapter)。
通過反編譯,可以發(fā)現(xiàn)FillSchema的關(guān)鍵處理步驟是在其調(diào)用私有方法FillSchemaFromCommand來完成的。簡單看一下該方法體的內(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}
首先,該操作含有一個數(shù)據(jù)庫的Query操作,這里其實是調(diào)用DBDataAdapter的SelectCommand的對象,執(zhí)行一次查詢,然后遍歷查詢返回的所有表,每遍歷到一個表的時候,通過該表的信息實例化一個SchemaMapping對象,再有該對象創(chuàng)建為DataSet/DataTable創(chuàng)建架構(gòu)信息。
這里,DataSet/DataTable是作為參數(shù)提供的,整個處理過程,首先必然的需要完成一次查詢操作,由于使用IDataReader,所以在查詢之后的所有操作期間,連接是保持著的,這一定程度上占用了一些資源(也可以說這些資源還不算太昂貴);其次,實例化一個SchemaMapping對象(該對象是內(nèi)部類,我在MSDN上沒有查到相關(guān)介紹性資料),我簡單看了一下這個類的代碼,在我看來,它的處理過程應該是占據(jù)了整個過程蠻大一部分資源的,這方面屬于個人見解。
由于我的認識上的有限,也為了保證文章的內(nèi)容無誤導,暫且說到這里。這個方法的進一步討論希望留給有興趣的朋友。
總結(jié)
以上是我對這兩個方法認識方面簡單的一個概括,其實從上面的描述,也打消了我原先認為的這兩個方法在獲取元數(shù)據(jù)上有本質(zhì)的差別。個人認為,之所以獲取結(jié)構(gòu)性元數(shù)據(jù)的消耗大,是因為獲取邏輯的繁瑣以及使用的對象的龐大,而參數(shù)信息相對而言完全屬于輕量級的東西,所以所謂性能上的差異并非因為獲取機制的本質(zhì)差異引起的。
?總結(jié)
以上是生活随笔為你收集整理的浅析ado.net获取数据库元数据信息的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 瓷牙多少钱一颗啊?
- 下一篇: 按拼音模糊匹配查询条件的生成类