2011-08-18 20 views
11

Tengo el siguiente código:foreach con SqlDataReader?

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

El código en el bucle foreach no se ejecuta. Sin embargo, puedo hacer esto:

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

Que funciona.

Obviamente, pude lograr el mismo resultado utilizando un bucle for para más que un bucle foreach, pero creo que la sintaxis foreach es más clara, así que la uso cuando sea posible.

¿Qué ha fallado aquí? ¿Los bucles foreach en C# no son tan flexibles como en lenguajes de alto nivel?

+0

No sé a ciencia cierta, pero yo asumir la foreach se iteración a través de cada campo en el conjunto de registros en lugar de cada registro ... – Chris

Respuesta

22

Algo como el siguiente. Tenga en cuenta que IDataReader deriva de IDataRecord que expone a los miembros utilizan para procesar la fila actual:

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

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

o incluso algo como lo siguiente, para obtener una enumeración o lista de entidad inflexible de tipos de objetos de un lector:

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

ACTUALIZACIÓN en respuesta al comentario de Caleb Bell.

Acepto Enumerate es un nombre mejor.

De hecho en mi biblioteca personal "común", he sido sustituidos lo anterior por un método de extensión en IDataReader:

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

Y la persona que llama puede obtener objetos de tipo fuerte usando:

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

Eso GetFromReader método es agradable y elegante, y es una pena que algo así no esté en el marco de forma nativa. Creé una versión del método de extensión y lo llamé Enumerate . –

+0

@CalebBell - Estoy de acuerdo 'Enumerate' es un nombre mejor, mira la actualización. – Joe

10

el foreach expone una IDataRecord, que le pone en un barco muy similar al bucle 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); 
     } 
    } 
} 

Si quieres ver algo más útil, necesitarás tener tu propio código que extraiga los valores del registro en algo más personalizado, como ha sugerido la otra respuesta.

De cualquier forma que necesite un código personalizado, ya sea que lo tenga en línea o no, o use un ciclo while o no depende de la frecuencia con que se va a escribir, supongo, más de una vez y probablemente deba pegarlo en un método de ayuda en alguna parte.

Y para responder a la pregunta un tanto: el problema no es el foreach, es su intento de uso de lo que devuelve para usted, ya que su uso comparable del bucle while no es realmente comparable.

+0

Ya veo. Eso es un poco molesto – Oliver

+0

@Marc rectificado. –

+0

@Marc "incorrecto - SqlDataReader implementa IEnumerable" - sí, pero esto enumera las columnas del IDataRecord (actual), no las filas. – Joe

0

puede hacer también que ...

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