Существуют ли какие-либо подводные камни при использовании IEnumerable<T> возвращаемый тип для данных SQL?

StackOverflow https://stackoverflow.com/questions/1400146

  •  05-07-2019
  •  | 
  •  

Вопрос

Мой вопрос касается состояния подключения к SQL, загрузки и т.д.на основе следующего кода:

public IEnumberable<MyType> GetMyTypeObjects()
{
  string cmdTxt = "select * from MyObjectTable";

  using(SqlConnection conn = new SqlConnection(connString))
  {
    using(SqlCommand cmd = new SqlCommand(cmdTxt, conn))
    {
      conn.Open();
      using(SqlDataReader reader = cmd.ExecuteReader())
      {
         while(reader.Read())
         {
            yield return Mapper.MapTo<MyType>(reader);
         }
       }
    }
  }
  yield break;
}

Я вижу, что это, возможно, является проблемой, если есть много процессов, выполняющих похожий код с длительным временем выполнения между итерациями объекта IEnumerable, потому что соединения будут открыты дольше и т.д.Однако также кажется правдоподобным, что это уменьшит загрузку процессора на сервере SQL, поскольку он возвращает данные только при использовании объекта IEnumerable.Это также снижает использование памяти клиентом, потому что клиенту нужно загружать только один экземпляр MyType во время его работы, а не загружать все вхождения MyType (путем перебора всего DataReader и возврата списка или чего-то еще).

  • Есть ли какие-либо случаи, о которых вы можете вспомнить, где вы не хотели бы использовать IEnumerable таким образом, или какие-либо случаи, которые, по вашему мнению, идеально подходят?

  • Какую нагрузку это накладывает на SQL-сервер?

  • Это то, что вы бы использовали в своем собственном коде (исключая любое упоминание о NHibernate, Subsonic и т.д.)?

  • -
Это было полезно?

Решение

Я бы не стал использовать его, потому что он скрывает, что происходит, и, возможно, может оставить подключения к базе данных без надлежащего удаления.

Объект connection будет закрыт, когда вы прочитаете последнюю запись, и если вы прекратите чтение до этого, объект connection не будет удален.Если вы, например, знаете, что у вас всегда есть десять записей в результате, и у вас просто есть цикл, который считывает эти десять записей из перечислителя без выполнения одиннадцатого вызова Read, который считывает дальше последнего элемента, соединение не закрывается должным образом.Кроме того, если вы хотели использовать только часть результата, у вас нет возможности закрыть соединение, не прочитав остальные записи.

Даже встроенные расширения для счетчиков могут вызвать это, даже если вы безупречно используете их правильно:

foreach (MyType item in GetMyTypeObjects().Take(10)) {
   ...
}

Другие советы

Это не тот шаблон, которому я бы следовал.Я бы не беспокоился так сильно о нагрузке на сервер, как о блокировках.Следование этому шаблону интегрирует процесс извлечения данных в поток вашей бизнес-логики, и это, похоже, верный путь к неприятностям;вы понятия не имеете, что происходит на стороне итерации, и вы вовлекаете в это себя.Извлеките ваши данные одним выстрелом, затем разрешите клиентскому коду выполнить перечисление поверх них, как только вы закроете программу чтения.

Я не рекомендую проводить предварительную оптимизацию.Во многих ситуациях соединения будут объединены в пул.

Я также не ожидаю никакой разницы в нагрузке на SQL Server - запрос уже будет скомпилирован и будет запущен.

Меня беспокоит то, что вы полностью отдаете себя на милость клиентского кода.

Вы уже упоминали, что вызывающий код может удерживать соединение открытым дольше, чем это строго необходимо, но также существует вероятность того, что он никогда не позволит закрыть / утилизировать объекты.

До тех пор, пока клиентский код использует foreach, using etc или явно вызывает перечислитель Dispose метод, тогда вы в порядке, но ничто не мешает ему делать что-то подобное:

var e = GetMyTypeObjects().GetEnumerator();
e.MoveNext();    // open the connection etc

// forget about the enumerator and go away and do something else
// now the reader, command and connection won't be closed/disposed
// until the GC kicks in and calls their finalisers
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top