2011-08-18 31 views
11

Tôi có đoạn mã sau:foreach với SqlDataReader?

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

Mã trong vòng lặp foreach không thực thi. Tuy nhiên, tôi có thể thực hiện việc này:

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

Tác phẩm nào hoạt động. Rõ ràng tôi có thể đạt được kết quả tương tự bằng cách sử dụng vòng lặp thông thường thay vì vòng lặp foreach, nhưng tôi nghĩ cú pháp foreach là rõ ràng hơn, vì vậy tôi sử dụng nó khi có thể.

Điều gì đã xảy ra ở đây? Các vòng foreach trong C# không linh hoạt như trong các ngôn ngữ cấp cao hơn?

+0

Tôi không biết chắc chắn nhưng tôi giả định foreach được lặp lại qua từng lĩnh vực trong recordset hơn là mỗi bản ghi ... – Chris

Trả lời

22

Giống như sau. Lưu ý rằng IDataReader xuất phát từ IDataRecord đó cho thấy nhiều thành viên sử dụng để xử lý hàng hiện tại:

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

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

Hoặc thậm chí một cái gì đó như sau, để có được một điều tra hoặc danh sách các thực thể mạnh mẽ, đánh máy các đối tượng từ một độc giả:

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(); 

CẬP NHẬT để trả lời nhận xét của Caleb Bell.

Tôi đồng ý Enumerate là tên tốt hơn.

Trong thực tế trong thư viện "chung" cá nhân của tôi, bây giờ tôi đã thay thế trên bằng một phương pháp mở rộng trên IDataReader:

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

Và người gọi có thể nhận được các vật mạnh mẽ, đánh máy sử dụng:

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

Đó GetFromReader phương pháp là tốt đẹp và thanh lịch, và nó là một sự xấu hổ rằng một cái gì đó như thế không phải là trong khuôn khổ nguyên bản! Tôi đã tạo một phiên bản phương thức mở rộng của nó và gọi nó là Enumerate . –

+0

@CalebBell - Tôi đồng ý 'Liệt kê' là tên tốt hơn, hãy xem cập nhật. – Joe

10

các foreach lộ một IDataRecord, mà đặt bạn trong một chiếc thuyền rất giống với các vòng lặp 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); 
     } 
    } 
} 

Nếu bạn muốn xem một cái gì đó hữu ích hơn, bạn sẽ cần phải có một số mã của riêng bạn mà trích xuất các giá trị từ bản ghi vào một cái gì đó tùy chỉnh hơn, như câu trả lời khác đã gợi ý. Dù bằng cách nào bạn sẽ cần mã tùy chỉnh, cho dù bạn có nội tuyến hay không hoặc sử dụng vòng lặp while hay không phụ thuộc vào mức độ thường xuyên viết, tôi cho rằng, nhiều hơn một lần và có lẽ bạn nên trong một phương thức trợ giúp ở đâu đó.

Và để trả lời câu hỏi phần nào: vấn đề không phải là foreach, đó là việc bạn sử dụng những gì nó trả về cho bạn, vì việc sử dụng so sánh vòng lặp while của bạn không thực sự so sánh được.

+0

Tôi hiểu. Đó là một chút khó chịu. – Oliver

+0

@Marc được sửa chữa. –

+0

@Marc "sai - SqlDataReader thực hiện IEnumerable" - có, nhưng điều này liệt kê các cột của (hiện tại) IDataRecord, không phải các hàng. – Joe

0

bạn có thể làm cũng là ...

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"]); 
      } 
    } 
}