Pergunta

Minha pergunta é relativa ao status de conexão SQL, carga etc. com base no seguinte código:

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;
}

Eu posso ver isso possivelmente sendo um problema se houver muitos processos executando código semelhante com longos tempos de execução entre as iterações do objeto ienumerable, porque as conexões estarão abertas por mais tempo etc. No entanto, também parece plausível que isso reduzirá o uso da CPU No servidor SQL, porque ele apenas retorna dados quando o objeto ienumerable é usado. Também reduz o uso da memória no cliente, porque o cliente só precisa carregar uma instância do MyType enquanto funciona, em vez de carregar todas as ocorrências do MyType (iterando todo o DataReader e retornando uma lista ou algo assim).

  • Há alguns casos em que você pode pensar em onde você não gostaria de usar o IENUMERABRILIVE DESTA CONSELHE, ou em qualquer instância que você acha que se encaixa perfeitamente?

  • Que tipo de carga isso coloca no servidor SQL?

  • Isso é algo que você usaria em seu próprio código (exceto qualquer menção ao Nibernate, Subsônico etc.)?

  • -
Foi útil?

Solução

Eu não o usaria, porque esconde o que está acontecendo e poderia deixar conexões de banco de dados sem o descarte adequado.

O objeto de conexão será fechado quando você tiver lido além do último registro e, se você parar de ler antes, o objeto de conexão não será descartado. Se você, por exemplo, souber que sempre tem dez registros em um resultado e apenas tem um loop que lê esses dez registros do enumerador sem fazer a décima primeira chamada que lê além do último item, a conexão não será fechada corretamente. Além disso, se você quiser usar apenas parte do resultado, não tem como fechar a conexão sem ler o restante dos registros.

Até as extensões embutidas para os enumeradores podem causar isso, mesmo que você as use corretamente:

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

Outras dicas

Este não é um padrão que eu seguiria. Eu não me preocuparia tanto em carga no servidor quanto em bloqueios. Seguir esse padrão integra o processo de recuperação de dados no fluxo de lógica de negócios da sua empresa, e isso parece uma receita geral para problemas; Você não tem idéia do que acontece no lado da iteração, e está se inserindo nele. Recupere seus dados de uma só vez e permita que o código do cliente o enumite quando você fechar o leitor.

Eu recomendo contra o pré-otimização. Em muitas situações, as conexões serão agrupadas.

Também não espero nenhuma diferença de carga no SQL Server - a consulta já será compilada e estará em execução.

Minha preocupação seria que você esteja se colocando completamente à mercê do código do cliente.

Você já mencionou que o código de chamada pode manter a conexão aberta por mais tempo do que é estritamente necessário, mas também existe a possibilidade de nunca permitir que os objetos fossem fechados/descartados.

Enquanto o código do cliente usar foreach, using etc ou chama explicitamente o enumerador Dispose Método então você está bem, mas não há nada para impedir que faça algo assim:

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
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top