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();
}
Что работает.
Очевидно, я мог бы достичь того же результата, используя регулярный цикл, а не цикл foreach, но я думаю, что синтаксис foreach более ясен, поэтому я использую его, когда это возможно.
Что здесь случилось? Являются ли циклы foreach в С# не такими гибкими, как на языках более высокого уровня?
Ответы
Ответ 1
Что-то вроде следующего. Обратите внимание, что IDataReader
происходит от IDataRecord
, который предоставляет членам, используемым для обработки текущей строки:
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))
Ответ 2
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 на самом деле не сравнимо.
Ответ 3
вы также можете сделать это...
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"]);
}
}
}