Pergunta

Estou apenas olhando a instrução using, sempre soube o que ela faz, mas até agora não tentei usá-la, criei o código abaixo:

 using (SqlCommand cmd = 
     new SqlCommand(reportDataSource, 
         new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString)))
 {
     cmd.CommandType = CommandType.StoredProcedure;
     cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
     cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
     cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
     cmd.Connection.Open();

     DataSet dset = new DataSet();
     new SqlDataAdapter(cmd).Fill(dset);
     this.gridDataSource.DataSource = dset.Tables[0];
 }

Isso parece funcionar, mas há algum sentido nisso, já que, pelo que sei, ainda precisaria incluir isso em um bloco try catch para capturar erros imprevistos, por exemplo.servidor sql inativo.Estou esquecendo de algo?

Até onde posso ver atualmente, isso apenas me impede de fechar e descartar o cmd, mas haverá mais linhas de código devido ao try catch ainda ser necessário.

Foi útil?

Solução

Esse código deve ser o seguinte para garantir o fechamento oportuno da conexão. Fechar apenas o comando não fecha a conexão:

using (SqlConnection con = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString))
using (SqlCommand cmd = new SqlCommand(reportDataSource, con))
         {
             cmd.CommandType = CommandType.StoredProcedure;
             cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
             cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
             cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
             cmd.Connection.Open();

             DataSet dset = new DataSet();
             new SqlDataAdapter(cmd).Fill(dset);
             this.gridDataSource.DataSource = dset.Tables[0];
         }

Para responder à sua pergunta, você pode fazer o mesmo em um bloco finalmente, mas isso esconde bem o código e garante que você se lembre de limpar.

Outras dicas

Ao fazer o trabalho de IO, codio -se para Espero uma exceção.

SqlConnection conn = null;
SqlCommand cmd = null;

try
{
    conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString)
    cmd = new SqlCommand(reportDataSource, conn);
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
    cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
    cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;

        conn.Open(); //opens connection

    DataSet dset = new DataSet();
    new SqlDataAdapter(cmd).Fill(dset);
    this.gridDataSource.DataSource = dset.Tables[0];
}
catch(Exception ex)
{
    Logger.Log(ex);
    throw;
}
finally
{
    if(conn != null)
        conn.Dispose();

        if(cmd != null)
        cmd.Dispose();
}

Editar: Para ser explícito, evito o usando Bloqueie aqui porque acredito que seja importante registrar situações como essa. A experiência me ensinou que você nunca sabe que tipo de exceção estranha pode surgir. O login nessa situação pode ajudá -lo a detectar um impasse ou descobrir onde uma mudança de esquema está impactando um pouco e pouco testado parte da sua base de código, ou qualquer número de outros problemas.

Editar 2: Pode -se argumentar que um bloco de uso pode envolver uma tentativa/captura nessa situação, e isso é completamente válido e funcionalmente equivalente. Isso realmente se resume à preferência. Deseja evitar o ninho extra ao custo de lidar com sua própria disposição? Ou você incorre no ninho extra para ter disposição automática. Eu sinto que o primeiro é mais limpo, então faço dessa maneira. No entanto, não reescrevo o último se encontrar na base de código em que estou trabalhando.

Editar 3: Eu realmente gostaria que a MS tivesse criado uma versão mais explícita do uso () que o tornou mais intuitivo o que realmente estava acontecendo e dado mais flexibilidade neste caso. Considere o seguinte código imaginário:

SqlConnection conn = null;
SqlCommand cmd = null;

using(conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString),
          cmd = new SqlCommand(reportDataSource, conn)
{
    conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString);
    cmd = new SqlCommand(reportDataSource, conn);
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
    cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
    cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
        cmd.Open();

    DataSet dset = new DataSet();
    new SqlDataAdapter(cmd).Fill(dset);
    this.gridDataSource.DataSource = dset.Tables[0];
}
catch(Exception ex)
{
    Logger.Log(ex);
    throw;
}

Uma declaração de uso apenas cria uma tentativa/finalmente com as chamadas Dispose () finalmente. Por que não dar ao desenvolvedor uma maneira unificada de fazer o descarte e o manuseio de exceções?

Pode não haver vantagem em usar um using declaração neste caso, se você vai ter um try/catch/finally Bloqueie de qualquer maneira. Como você sabe, o using a declaração é açúcar sintático para um try/finally que descarta o IDisposable objeto. Se você vai ter o seu próprio try/finally Enfim, você certamente pode fazer o Dispose você mesma.

Isso realmente se resume principalmente ao estilo - sua equipe pode se sentir mais confortável com using declarações ou using As declarações podem fazer com que o código pareça mais limpo.

Mas, se o caldeira do caldeiro using A declaração estaria escondendo está lá de qualquer maneira, vá em frente e lidar com as coisas você mesmo, se essa for a sua preferência.

Se o seu código se parece com o seguinte:

using (SqlCommand cmd = new SqlCommand(...))
{
  try
  {
    /* call stored procedure */
  }
  catch (SqlException ex)
  {
    /* handles the exception. does not rethrow the exception */
  }
}

Então eu refatoraria para usar, tente .. pegue .. finalmente.

SqlCommand cmd = new SqlCommand(...)
try
{
  /* call stored procedure */
}
catch (SqlException ex)
{
  /* handles the exception and does not ignore it */
}
finally
{
   if (cmd!=null) cmd.Dispose();
}

Nesse cenário, eu estaria lidando com a exceção, para que não tenha escolha a não ser adicionar essa tentativa. Observe que devo estar fazendo algo no bloco de captura e não apenas ignorando a exceção.

Elaborando o que Chris Ballance disse, a especificação C# (ECMA-334 versão 4) Seção 15.13 declara "uma declaração de uso é traduzida em três partes: aquisição, uso e descarte. O uso do recurso é implicitamente incluído em uma declaração de tentativa que inclui uma cláusula finalmente. Esta cláusula finalmente descarta o recurso. Se um recurso nulo for adquirido, nenhuma chamada para descartar será feita e nenhuma exceção será lançada ".

A descrição é de quase 2 páginas - vale a pena ler.

Na minha experiência, o SQLConnection/SQLCommand pode gerar erros de tantas maneiras que você quase precisa lidar com as exceções jogadas mais do que lidar com o comportamento esperado. Não tenho certeza se gostaria de usar a cláusula aqui, pois eu gostaria de lidar com o caso de recursos nulos.

Usar não é sobre capturar exceções. Trata -se de descartar corretamente os recursos que estão fora da visão do coletor de lixo.

Um problema com "Usando" é que ele não lida com exceções. Se os designers de "usando" adicionariam "capturar" opcionalmente à sua sintaxe, como abaixo do pseudocode, seria muito mais útil:

using (...MyDisposableObj...)
{

   ... use MyDisposableObj ...

catch (exception)

   ... handle exception ...

}

it could even have an optional "finally" clause to cleanup anything other than the "MyDisposableObj" allocated at the beginning of the "using" statement... like:

using (...MyDisposableObj...)
{

   ... use MyDisposableObj ...
   ... open a file or db connection ...

catch (exception)

   ... handle exception ...

finally

   ... close the file or db connection ...

}

Ainda não haverá necessidade de escrever código para descartar MyDisposableObj b/c seria tratado por using...

Como isso?

Sim, você ainda precisaria capturar exceções. O benefício do bloco de uso é que você está adicionando escopo ao seu código. Você está dizendo: "Dentro deste bloco de código, faça algumas coisas e quando chegar ao fim, feche e descarte os recursos"

Não é completamente necessário, mas define suas intenções para qualquer outra pessoa que use seu código, e também ajuda a não deixar conexões etc. aberta por engano.

Há muitas respostas excelentes aqui, mas acho que isso ainda não foi dito.

Não importa o quê ... o método "Dispone" será chamado no objeto no bloco "usando". Se você colocar uma declaração de devolução ou lançar um erro, o "disposição" será chamado.

Exemplo:

Fiz uma aula chamada "MyDisposable", e ela implementa idispossável e simplesmente faz um console.Write. Isto sempre escreve no console mesmo em todos esses cenários:

using (MyDisposable blah = new MyDisposable())
{
    int.Parse("!"); // <- calls "Dispose" after the error.

    return; // <-- calls Dispose before returning.
}

A instrução de uso é realmente alterada para um bloqueio Try/Finalmente pelo compilador no qual o parâmetro do bloco de uso é descartado desde que implementa a interface idisposlável. Além de garantir que os objetos especificados sejam descartados corretamente quando caem fora do escopo, realmente não há erro de captura de erro obtido usando esse construto.

Como é mencionado por TheSoftwareJedi Acima, convém garantir que os objetos SQLConnection e SQLCommand sejam descartados adequadamente. Empilhar em um único bloco de uso é um pouco confuso e pode não fazer o que você acha que faz.

Além disso, lembre -se de usar o bloco Try/Catch como lógica. É um cheiro de código pelo qual meu nariz tem uma antipatia particular, e frequentemente usada por novatos ou por nós com muita pressa de cumprir um prazo.

Para sua informação, neste exemplo específico, porque você está usando um objeto de conexão e comando do ADO.NET, esteja ciente de que a instrução Usando apenas executa o comando.Dispose, e a conexão.dispose () que na verdade não fecha a conexão, mas Simplesmente libera -o de volta ao pool de conexão ADO.NET para ser reutilizado pela próxima conexão. O coletor o libera de volta à piscina, o que pode não ser até inúmeras outras solicitações de conexão, que de outra forma seriam forçadas a criar novas conexões, mesmo que haja uma não utilizada esperando para ser coletada de lixo.

Eu tomaria minha decisão sobre quando e quando não usar a declaração de uso dependente do recurso com o qual estou lidando. No caso de um recurso limitado, como uma conexão ODBC, eu preferiria usar T/C/F para que eu possa registrar erros significativos no ponto em que ocorreram. Deixar que os erros do driver do banco de dados voltem ao cliente e potencialmente sejam perdidos no embrulho de exceção de nível superior é sub -ideal.

O T/C/F dá a você tranqüilidade de que o recurso está sendo tratado da maneira que você deseja. Como alguns já mencionaram, a declaração de uso não fornece o manuseio de exceções, apenas garante que o recurso seja destruído. O manuseio de exceção é uma estrutura de linguagem subestilizada e subestimada que geralmente é a diferença entre o sucesso e a falha de uma solução.

Se o chamador da sua função for responsável por lidar com quaisquer exceções, a instrução using é uma boa maneira de garantir que os recursos sejam limpos, independentemente do resultado.

Ele permite que você coloque o código de tratamento de exceções nos limites da camada/montagem e ajuda a evitar que outras funções fiquem muito confusas.

Claro, isso realmente depende dos tipos de exceções lançadas pelo seu código.Às vezes você deve usar try-catch-finally em vez de uma instrução using.Meu hábito é sempre começar com uma instrução using para IDisposables (ou fazer com que classes que contenham IDisposables também implementem a interface) e adicionar try-catch-finally conforme necessário.

Então, basicamente, "usar" é exatamente o mesmo que "Try/Catch/Finalmente", apenas muito mais flexível para manuseio de erros.

Correção pequena com o exemplo: SqlDataAdapter também precisa ser instanciado em um using declaração:

using (SqlConnection con = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString))
using (SqlCommand cmd = new SqlCommand(reportDataSource, con))
{
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
    cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
    cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
    con.Open();

    DataSet dset = new DataSet();
    using (SqlDataAdapter adapter = new SqlDataAdapter(cmd))
    {
        adapter.Fill(dset);
    }
    this.gridDataSource.DataSource = dset.Tables[0];
}

Primeiro, seu exemplo de código deve ser:

using (SqlConnection conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString))
using (SqlCommand cmd = new SqlCommand(reportDataSource, conn))
{
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
    cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
    cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
    cmd.Connection.Open();

    DataSet dset = new DataSet();
    new SqlDataAdapter(cmd).Fill(dset);
    this.gridDataSource.DataSource = dset.Tables[0];
}

Com o código em sua pergunta, uma exceção criando o comando resultará na conexão recém-criada não sendo descartada. Com o exposto acima, a conexão é descartada corretamente.

Se você precisar lidar com exceções em construção da conexão e comando (assim como quando usá -los), sim, você precisa envolver a coisa toda em uma tentativa/captura:

try
{
    using (SqlConnection conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString))
    using (SqlCommand cmd = new SqlCommand(reportDataSource, conn))
    {
        cmd.CommandType = CommandType.StoredProcedure;
        cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
        cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
        cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
        cmd.Connection.Open();

        DataSet dset = new DataSet();
        new SqlDataAdapter(cmd).Fill(dset);
        this.gridDataSource.DataSource = dset.Tables[0];
    }
}
catch (RelevantException ex)
{
    // ...handling...
}

Mas você não precisa lidar com a limpeza conn ou cmd; Já foi feito para você.

Contraste com a mesma coisa sem using:

SqlConnection conn = null;
SqlCommand cmd = null;
try
{
    conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString);
    cmd = new SqlCommand(reportDataSource, conn);
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
    cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
    cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
    cmd.Connection.Open();

    DataSet dset = new DataSet();
    new SqlDataAdapter(cmd).Fill(dset);
    this.gridDataSource.DataSource = dset.Tables[0];
}
catch (RelevantException ex)
{
    // ...handling...
}
finally
{
    if (cmd != null)
    {
        try
        {
            cmd.Dispose();
        }
        catch { }
        cmd = null;
    }
    if (conn != null)
    {
        try
        {
            conn.Dispose();
        }
        catch { }
        conn = null;
    }
}
// And note that `cmd` and `conn` are still in scope here, even though they're useless

Eu sei o que prefiro escrever. :-)

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top