Это широко распространенный шаблон ADO.NET для извлечения данных из базы данных с помощью устройства чтения данных, но, как ни странно, он не работает.
Не работает:
public static IEnumerable<IDataRecord> SelectDataRecord<T>(string query, string connString)
                                                          where T : IDbConnection, new()
{
    using (var conn = new T())
    {
        using (var cmd = conn.CreateCommand())
        {
            cmd.CommandText = query;
            cmd.Connection.ConnectionString = connString;
            cmd.Connection.Open();
            using (var reader = (DbDataReader)cmd.ExecuteReader())
            {
                // the main part
                while (reader.Read())
                {
                    yield return (IDataRecord)reader;
                }
            }
        }
    }
Это работает:
public static IEnumerable<IDataRecord> SelectDataRecord<T>(string query, string connString)
                                                          where T : IDbConnection, new()
{
    using (var conn = new T())
    {
        using (var cmd = conn.CreateCommand())
        {
            cmd.CommandText = query;
            cmd.Connection.ConnectionString = connString;
            cmd.Connection.Open();
            using (var reader = (DbDataReader)cmd.ExecuteReader())
            {
                // the main part
                foreach (var item in reader.Cast<IDataRecord>())
                {
                    yield return item;
                }
            }
        }
    }
Единственное соответствующее изменение, которое я вижу, это то, что в первом коде итератор возвращается из цикла while, а во втором - из цикла foreach.
Я называю это следующим образом:
// I have to buffer for some reason
var result = SelectDataRecord<SQLiteConnection>(query, connString).ToList(); 
foreach(var item in result)
{
    item.GetValue(0); // explosion
}
Я пробовал с SQLite коннектор .NET, а также MySQL. Результат тот же, то есть первый подход сбой, второй успешный.
Exception
SQLite
Необработанное исключение типа "Исправление System.InvalidOperationException" произошло в System.Data.SQLite.dll. Дополнительная информация: Нет текущей строки
MySQL
Необработанное исключение типа "System.Exception" произошло в MySql.Data.dll. Дополнительная информация: Нет текущего запроса в считывателе данных
Это из-за различий в реализации между reader.Read и reader.GetEnumerator в конкретных соединителях ADO.NET? Я не видел заметной разницы, когда я проверил источник проекта System.Data.SQLite, GetEnumerator вызывает Read внутренне. В идеале я предполагаю, что в обоих случаях ключевое слово yield предотвращает нетерпеливое выполнение метода, и циклы должны выполняться только после того, как перечисление перечислили извне.
Обновление:
Я использую этот шаблон как безопасный (по существу такой же, как и второй подход, но немного менее подробный),
using (var reader = cmd.ExecuteReader())
    foreach (IDataRecord record in reader as IEnumerable)
        yield return record;
