2011-08-18 87 views
11

我有以下代碼:foreach與SqlDataReader?

SqlDataReader reader = getAddressQuery.sqlReader; 
while (reader.Read()) 
{ 
    foreach (Object ob in reader) 
    { 
     someText.InnerText = someText.InnerText + " " + ob.ToString(); 
    } 
} 

在foreach循環中的代碼不會執行。不過,我可以這樣做:

SqlDataReader reader = getAddressQuery.sqlReader; 
while (reader.Read()) 
{ 
    someText.InnerText = reader[0].ToString(); 
} 

哪個有效。

很明顯,我可以使用常規for循環而不是foreach循環來實現相同的結果,但我認爲foreach語法更清晰,所以我儘可能使用它。

這裏出了什麼問題? c#中的foreach循環不像更高級的語言那樣靈活?

+0

我不知道肯定,但我認爲在foreach通過每個字段中記錄迭代,而不是每個記錄... – Chris

回答

22

類似於以下內容。需要注意的是IDataReaderIDataRecord暴露成員用來處理當前行派生:

IEnumerable<IDataRecord> GetFromReader(IDataReader reader) 
{ 
    while(reader.Read()) yield return reader; 
} 

foreach(IDataRecord record in GetFromReader(reader)) 
{ 
    ... process it ... 
} 

甚至像下面這樣,得到強類型實體的枚舉或列表從一個讀者對象

IEnumerable<T> GetFromReader<T>(IDataReader reader, Func<IDataRecord, T> processRecord) 
{ 
    while(reader.Read()) yield return processRecord(reader); 
} 

MyType GetMyTypeFromRecord(IDataRecord record) 
{ 
    MyType myType = new MyType(); 
    myType.SomeProperty = record[0]; 
    ... 
    return myType; 
} 

IList<MyType> myResult = GetFromReader(reader, GetMyTypeFromRecord).ToList(); 

UPDATE迴應Caleb Bell的評論。

我同意Enumerate是一個更好的名字。

其實在我個人的「常用」庫,我現在已經取代了上述通過擴展方法上IDataReader

public static IEnumerable<IDataRecord> Enumerate(this IDataReader reader) 
{ 
    while (reader.Read()) 
    { 
     yield return reader; 
    } 
} 

呼叫者可使用獲得強類型的對象:

reader.Enumerate.Select(r => GetMyTypeFromRecord(r)) 
+0

這GetFromReader 方法是不錯的和優雅的,這是類似的東西不是在框架本身!我創建了它的擴展方法版本,並將其命名爲Enumerate 。 –

+0

@CalebBell - 我同意'枚舉'是一個更好的名字,請參閱更新。 – Joe

10

foreach暴露了IDataRecord,這使你在一個非常類似的船到while循環:

using (SqlConnection conn = new SqlConnection("")) 
using (SqlCommand comm = new SqlCommand("select * from somewhere", conn)) 
{ 
    conn.Open(); 

    using (var r = comm.ExecuteReader()) 
    { 
     foreach (DbDataRecord s in r) 
     { 
      string val = s.GetString(0); 
     } 
    } 
} 

如果你想看到更有用的東西,你需要有一些你自己的代碼,將記錄中的值提取到更自定義的東西上,正如其他答案所暗示的那樣。

無論哪種方式,您都需要自定義代碼,無論是否使用內聯或使用while循環取決於它將被寫入的頻率我認爲,不止一次,你應該堅持它在一個輔助方法的地方。

爲了回答這個問題:問題不是foreach,它是你試圖使用它返回給你的東西,因爲你可比較的while循環使用實際上沒有可比性。

+0

我明白了。這有點煩人。 – Oliver

+0

@Marc糾正。 –

+0

@Marc「錯誤 - SqlDataReader確實實現了IEnumerable」 - 是的,但是這列舉了(當前)IDataRecord的列,而不是行。 – Joe

0

,你也可以做到這一點...

string sql = "select * from Users"; 
using (SqlConnection conn = GetConnection()){ 

    conn.Open(); 
    using (SqlDataReader rdr = new SqlCommand(sql, conn).ExecuteReader()){ 

      foreach (DbDataRecord c in rdr.Cast<DbDataRecord>()){ 
       Console.Write("{0} {1} ({2}) - ", (string)c["Name"], (string)c["Surname"], (string)c["Mail"]); 
       Console.WriteLine((string)c["LoginID"]); 
      } 
    } 
}