2010-05-21 44 views
15

當從DataReader返回數據我通常使用的DataReader序參考抓住相關的列:DataReader - 硬編碼序數?

if (dr.HasRows)   
    Console.WriteLine(dr[0].ToString()); 

if (dr.HasRows)   
    Console.WriteLine(dr.GetString(0)); 

if (dr.HasRows)   
    Console.WriteLine((string)dr[0]); 

我一直這樣做是因爲我在早期被告知使用dr["ColumnName"]或更多el索引方式導致性能下降。

然而,雖然數據實體的所有引用越來越強類型的,我覺得這個更不舒服。我也知道上述不檢查DBNull

什麼是從DataReader返回數據的最可靠的方法?

+0

相關性能問題:[sqldatareader-are-these-two-the-same-which-one-is-faster](http:// stackoverflow。com/questions/7831574/sqldatareader-are-these-two-the-same-which-one-is-faster) – nawfal 2013-11-28 07:56:18

回答

25

在這種情況下有可能爭論雙方。正如其他人已經指出的那樣,如果有人更改底層數據庫中列的順序,使用該名稱更具可讀性並且不會中斷。但有人可能會爭辯說,如果某人更改了底層數據庫中的列名,那麼使用序號具有不會中斷的優勢。不過,我更喜歡前者的觀點,並認爲列名的可讀性參數勝過了第二個參數。而且名稱的另一個參數是它可以「自我檢測」錯誤。如果有人確實更改了字段名稱,那麼代碼有更好的中斷機會,而不是在錯誤的字段中顯示工作的細微錯誤。

看起來很明顯,但也許值得一提的是一個既有自我檢測錯誤又有序號性能的使用案例。如果在SQL顯式指定SELECT列表中,然後使用序號不會因爲在代碼中陳述保證的順序是一個問題:

SELECT name, address, phone from mytable 

在這種情況下,這將是相當安全的使用序號來訪問數據。如果有人在桌子周圍移動場地,這並不重要。如果有人更改名稱,那麼SQL語句在運行時會產生錯誤。

最後一點。我剛剛參加了我幫助編寫的供應商的測試。該測試讀取100萬行並訪問每個記錄上的「姓氏」字段(與值進行比較)。 rdr[「lastname」]的使用耗時3301毫秒,而rdr.GetString(1)耗費2640毫秒(約加速25%)。在這個特定的提供程序中,名稱查找使用排序查找將名稱翻譯爲序號。

+0

這裏您有一些優點。 – 2010-05-24 16:29:52

+4

+1提供指標。 – 2010-05-24 20:47:14

+0

就我個人而言,我認爲重新排列列(讀取,插入新字段並因此重新排列現有列的位置)比重命名列名更爲常見。所以你的建議+1。 – nawfal 2015-07-31 13:58:53

0

序號的問題是如果列的順序發生改變,並且您不得不修改DataReader的使用者代碼,這與使用列名稱不同。

我不認爲使用序號或列名時,有一個性能增益,它更多的是對最佳實踐和編碼標準和代碼的可維護性真的

4

我總是以字符串名稱的方法去只是因爲閱讀的代碼清潔器。不得不從心理上解析索引到列名是非常可怕的。

+4

使用列名稱也消除了「幻數」,並使程序員的工作更輕鬆。使用序數是一種過早的微觀優化。 – Malfist 2010-05-21 13:21:16

+1

+1。列名更改比其次序更難得 – abatishchev 2010-05-21 14:38:59

+0

這個問題有兩個部分,性能方面和可維護性/可讀性。字符串名稱不能很好地縮放。 @Malfist將某些「過早微優化」標記爲與您的代碼相關。如果你的代碼性能很高,那麼這絕對是瓶頸。 – Josh 2010-05-22 11:34:14

2

我認爲索引的字段是更好的方法,如果它只會是避免從底層數據庫,因爲你硬編碼的字段名稱這需要您的應用程序重新編譯字段名稱的變化。

對於每個字段,您都需要手動檢查空值。

var dr = command.ExecuteQuery(); 

if (dr.HasRows) { 
    var customer = new Customer(); 
    // I'm assuming that you know each field's position returned from your query. 
    // Where comes the importance to write all of your selected fields and not just "*" or "ALL". 
    customer.Id = dr[0] == null || dr[0] == DBNull.Value ? null : Convert.ToInt32(dr[0]); 
    ... 
} 

除了它,它會允許你使用反射,使這個「的GetData()」方法更通用提供的typeof(T),並得到了正確的類型適當的構造函數。對每個列的順序的綁定是有些人希望避免的唯一事情,但在這種情況下,它變得有價值。

4

按名稱對數據讀取器進行索引的成本稍高一些。這有兩個主要原因。

  • 典型實現將字段信息存儲在使用數字索引的數據結構中。必須進行映射操作才能將名稱轉換爲數字。
  • 某些實現將對名稱執行雙向查找。第一次嘗試匹配字段名稱並打開區分大小寫。如果該傳球失敗,則第二輪傳球從關閉大小寫敏感開始。

但是,在大多數情況下,通過名稱查找字段導致的性能損失與數據庫執行命令所花費的時間相關。不要讓性能損失決定您在名稱和數字索引之間的選擇。

儘管輕微的性能損失,我通常選擇名稱索引有兩個原因。

  • 該代碼更容易閱讀。
  • 該代碼對結果集模式中的更改容忍度更高。

如果你覺得自己的名字索引的性能損失成爲問題(也許是命令迅速地執行,而是返回很多行),然後通過名稱查找的數字索引一次,保存它拿走,並用它來剩下的行。

13

字符串名稱查找比序號調用要昂貴得多,但比硬編碼序號更易於維護和「脆弱」。所以這就是我一直在做的事情。這是兩全其美的。我不必記住序號值或關心列順序是否改變,但我可以獲得使用序號的性能優勢。

var dr = command.ExecuteQuery(); 
if (dr.HasRows) 
{ 
    //Get your ordinals here, before you run through the reader 
    int ordinalColumn1 = dr.GetOrdinal("Column1"); 
    int ordinalColumn2 = dr.GetOrdinal("Column2"); 
    int ordinalColumn3 = dr.GetOrdinal("Column3"); 

    while(dr.Read()) 
    { 
     // now access your columns by ordinal inside the Read loop. 
     //This is faster than doing a string column name lookup every time. 
     Console.WriteLine("Column1 = " + dr.GetString(ordinalColumn1); 
     Console.WriteLine("Column2 = " + dr.GetString(ordinalColumn2); 
     Console.WriteLine("Column3 = " + dr.GetString(ordinalColumn3); 
    } 
} 

注:本才真正有意義,你期望在行的體面數讀者GetOrdinal()調用是多餘的,只有當你結合的儲蓄從循環中調用GetString(int ordinalNumber)支付本身更大。比調用GetOrdinal的成本要高。

編輯:錯過了這個問題的第二部分。關於DBNull值,我已經開始編寫處理這種可能性的擴展方法。例如:dr.GetDatetimeSafely()在這些擴展方法中,你可以做任何你需要的東西來確信你能夠獲得預期的價值。

+0

Upvoted兩全其美。 – nawfal 2015-07-31 18:36:57

+1

當我不得不潛入ADO.NET時,我喜歡這種方法。我創建一個內聯對象並將所有序號映射爲友好名稱,以便我可以執行像'reader.GetString(ordinals.CustomerName)' – 2016-04-19 14:16:17