dalam blok "menggunakan" apakah SqlConnection ditutup pada pengembalian atau pengecualian?

137

Pertanyaan pertama:
Katakanlah saya punya

using (SqlConnection connection = new SqlConnection(connectionString))
{
    connection.Open();

    string storedProc = "GetData";
    SqlCommand command = new SqlCommand(storedProc, connection);
    command.CommandType = CommandType.StoredProcedure;
    command.Parameters.Add(new SqlParameter("@EmployeeID", employeeID));

    return (byte[])command.ExecuteScalar();
}

Apakah koneksi ditutup? Karena secara teknis kami tidak pernah mencapai yang terakhir }seperti returnsebelumnya.

Pertanyaan kedua:
Kali ini saya punya:

try
{
    using (SqlConnection connection = new SqlConnection(connectionString))
    {
        int employeeID = findEmployeeID();

        connection.Open();
        SqlCommand command = new SqlCommand("UpdateEmployeeTable", connection);
        command.CommandType = CommandType.StoredProcedure;
        command.Parameters.Add(new SqlParameter("@EmployeeID", employeeID));
        command.CommandTimeout = 5;

        command.ExecuteNonQuery();
    }
}
catch (Exception) { /*Handle error*/ }

Sekarang, katakan di suatu tempat di trykita mendapat kesalahan dan itu tertangkap. Apakah koneksi masih tertutup? Karena sekali lagi, kami melewatkan sisa kode di trydan langsung ke catchpernyataan.

Apakah saya berpikir terlalu linier dalam cara usingkerjanya? yaitu Apakah Dispose()langsung dipanggil saat kita meninggalkan usingruang lingkup?

Marcus
sumber

Jawaban:

181
  1. Iya
  2. Iya.

Either way, ketika menggunakan blok keluar (baik dengan penyelesaian yang berhasil atau karena kesalahan) itu ditutup.

Meskipun menurut saya akan lebih baik untuk mengatur seperti ini karena jauh lebih mudah untuk melihat apa yang akan terjadi, bahkan untuk programmer pemeliharaan baru yang akan mendukungnya nanti:

using (SqlConnection connection = new SqlConnection(connectionString)) 
{    
    int employeeID = findEmployeeID();    
    try    
    {
        connection.Open();
        SqlCommand command = new SqlCommand("UpdateEmployeeTable", connection);
        command.CommandType = CommandType.StoredProcedure;
        command.Parameters.Add(new SqlParameter("@EmployeeID", employeeID));
        command.CommandTimeout = 5;

        command.ExecuteNonQuery();    
    } 
    catch (Exception) 
    { 
        /*Handle error*/ 
    }
}
David
sumber
3
@TrueWill - Saya setuju. Saya baru saja memindahkan kode sedikit untuk struktur.
David
10
Pertanyaan: Apakah saya bahkan perlu MEMBUKA koneksi saat menggunakan pernyataan Using?
Fandango68
3
Juga jika Anda menggunakan transaksi, dengan memiliki di try catchdalam usingAnda dapat secara eksplisit .Commitatau .Rollbacktransaksi di catch. Ini lebih mudah dibaca dan eksplisit, dan memungkinkan Anda untuk berkomitmen jika itu masuk akal dengan diberikan jenis pengecualian. (Transaksi secara implisit berputar kembali conn.Closejika tidak dilakukan.).
Chris
9
@ Fernando68 Ya, Anda masih memiliki Openkoneksi. usinghanya menjamin bahwa metode objek Disposedipanggil.
juharr
Saya memiliki ExecuteScalar kembali di dalam menggunakan blok. Dan ketika saya menjalankan metode kedua kalinya ini sangat cepat, seperti koneksi terbuka. Mengapa sangat cepat untuk kedua kalinya?
perspektif positif
47

Ya untuk kedua pertanyaan tersebut. Pernyataan using dikompilasi menjadi blok coba / akhirnya

using (SqlConnection connection = new SqlConnection(connectionString))
{
}

sama dengan

SqlConnection connection = null;
try
{
    connection = new SqlConnection(connectionString);
}
finally
{
   if(connection != null)
        ((IDisposable)connection).Dispose();
}

Sunting: Memperbaiki cast ke Disposable http://msdn.microsoft.com/en-us/library/yh598w02.aspx

Ryan Pedersen
sumber
tidak persis seperti itu, tetapi cukup dekat. perbedaan tepatnya tidaklah penting.
Bryan
@Bryan tidak mengerti, dapatkah Anda menyebutkan perbedaan tepatnya, dapat membantu kami lebih bersandar :-)
mohits00691
Wah, itu komentar yang dibuat lama sekali :) Sepertinya ada yang diedit sehari setelah saya membuat komentar itu. Saya pikir itulah perbedaan yang saya pikirkan.
Bryan
@Bryan Ya, saya tetap melakukan penyesuaian setelah komentar Anda.
Ryan Pedersen
17

Ini Template saya. Semua yang Anda butuhkan untuk memilih data dari server SQL. Koneksi ditutup dan dibuang dan kesalahan dalam koneksi dan eksekusi tertangkap.

string connString = System.Configuration.ConfigurationManager.ConnectionStrings["CompanyServer"].ConnectionString;
string selectStatement = @"
    SELECT TOP 1 Person
    FROM CorporateOffice
    WHERE HeadUpAss = 1 AND Title LIKE 'C-Level%'
    ORDER BY IntelligenceQuotient DESC
";
using (SqlConnection conn = new SqlConnection(connString))
{
    using (SqlCommand comm = new SqlCommand(selectStatement, conn))
    {
        try
        {
            conn.Open();
            using (SqlDataReader dr = comm.ExecuteReader())
            {
                if (dr.HasRows)
                {
                    while (dr.Read())
                    {
                        Console.WriteLine(dr["Person"].ToString());
                    }
                }
                else Console.WriteLine("No C-Level with Head Up Ass Found!? (Very Odd)");
            }
        }
        catch (Exception e) { Console.WriteLine("Error: " + e.Message); }
        if (conn.State == System.Data.ConnectionState.Open) conn.Close();
    }
}

* Direvisi: 2015-11-09 *
Seperti yang disarankan oleh NickG; Jika terlalu banyak kawat gigi yang mengganggu Anda, format seperti ini ...

using (SqlConnection conn = new SqlConnection(connString))
   using (SqlCommand comm = new SqlCommand(selectStatement, conn))
   {
      try
      {
         conn.Open();
         using (SqlDataReader dr = comm.ExecuteReader())
            if (dr.HasRows)
               while (dr.Read()) Console.WriteLine(dr["Person"].ToString());
            else Console.WriteLine("No C-Level with Head Up Ass Found!? (Very Odd)");
      }
      catch (Exception e) { Console.WriteLine("Error: " + e.Message); }
      if (conn.State == System.Data.ConnectionState.Open) conn.Close();
   }

Kemudian lagi, jika Anda bekerja untuk game EA atau DayBreak, Anda juga dapat mengabaikan jeda baris karena itu hanya untuk orang-orang yang harus kembali dan melihat kode Anda nanti dan siapa yang benar-benar peduli? Apakah saya benar? Maksud saya 1 baris, bukan 23 berarti saya programmer yang lebih baik, bukan?

using (SqlConnection conn = new SqlConnection(connString)) using (SqlCommand comm = new SqlCommand(selectStatement, conn)) { try { conn.Open(); using (SqlDataReader dr = comm.ExecuteReader()) if (dr.HasRows) while (dr.Read()) Console.WriteLine(dr["Person"].ToString()); else Console.WriteLine("No C-Level with Head Up Ass Found!? (Very Odd)"); } catch (Exception e) { Console.WriteLine("Error: " + e.Message); } if (conn.State == System.Data.ConnectionState.Open) conn.Close(); }

Fiuh ... Oke. Saya mendapatkan itu dari sistem saya dan saya selesai menghibur diri untuk sementara waktu. Lanjut.

ShaneLS
sumber
6
Tahukah Anda bahwa Anda dapat menumpuk menggunakan pernyataan tanpa tanda kurung kurawal tambahan? Hapus tanda kurung kurawal terakhir, kemudian tempatkan pernyataan
use di
Ya pak. Terima kasih. Saya sadar tetapi ingin kode saya menunjukkan dengan tepat apa yang terjadi tanpa menggunakan terlalu banyak jalan pintas lainnya. Catatan bagus untuk ditambahkan ke pembaca akhir.
ShaneLS
Mengapa Anda menggunakan conn.Close();di akhir? Bukankah usingpernyataan melakukannya untuk Anda melalui pembuangan?
Fredrick Gauss
Saya yakin sekarang sudah (sejak .net 3.5). Tidak jelas bagi saya sejak awal dengan .net 2.0 jadi saya membiasakan diri untuk memeriksa dan menutup.
ShaneLS
2
"berarti 1 baris bukannya 23 berarti saya programmer yang lebih baik, bukan?" Saya suka Anda :-D
Philipp Müller
5

Buang hanya akan dipanggil saat Anda meninggalkan ruang lingkup penggunaan. Maksud dari "menggunakan" adalah untuk memberi pengembang cara yang terjamin untuk memastikan bahwa sumber daya dibuang.

Dari MSDN :

Pernyataan using bisa keluar baik saat akhir pernyataan using tercapai atau jika pengecualian dilemparkan dan kontrol meninggalkan blok pernyataan sebelum akhir pernyataan.

overstood
sumber
5

Usingmenghasilkan percobaan / akhirnya di sekitar objek yang dialokasikan dan panggilan Dispose()untuk Anda.

Anda tidak perlu repot membuat blokir dan panggilan coba / akhirnya secara manual Dispose()

VoodooChild
sumber
3

Dalam contoh pertama Anda, kompilator C # sebenarnya akan menerjemahkan pernyataan using sebagai berikut:

SqlConnection connection = new SqlConnection(connectionString));

try
{
    connection.Open();

    string storedProc = "GetData";
    SqlCommand command = new SqlCommand(storedProc, connection);
    command.CommandType = CommandType.StoredProcedure;
    command.Parameters.Add(new SqlParameter("@EmployeeID", employeeID));

    return (byte[])command.ExecuteScalar();
}
finally
{
    connection.Dispose();
}

Akhirnya pernyataan akan selalu dipanggil sebelum fungsi kembali dan koneksi akan selalu ditutup / dibuang.

Jadi, dalam contoh kedua Anda, kode akan dikompilasi menjadi yang berikut:

try
{
    try
    {
        connection.Open();

        string storedProc = "GetData";
        SqlCommand command = new SqlCommand(storedProc, connection);
        command.CommandType = CommandType.StoredProcedure;
        command.Parameters.Add(new SqlParameter("@EmployeeID", employeeID));

        return (byte[])command.ExecuteScalar();
    }
    finally
    {
        connection.Dispose();
    }
}
catch (Exception)
{
}

Pengecualian akan tertangkap dalam pernyataan akhirnya dan koneksi ditutup. Pengecualian tidak akan terlihat oleh klausa tangkapan luar.

Kerri Brown
sumber
1
contoh yang sangat bagus kawan, tetapi saya harus tidak setuju dengan komentar terakhir Anda, jika pengecualian terjadi dalam blok penggunaan, itu akan ditangkap tanpa masalah pada tangkapan luar, sebenarnya saya mengujinya dengan menulis 2 menggunakan blok di dalam blok coba / tangkap , dan yang mengejutkan saya, saya mendapatkan pesan kesalahan pengecualian saya yang muncul dari blok penggunaan detik bagian dalam.
WhySoSerious
1

Saya menulis dua pernyataan menggunakan di dalam blok coba / tangkap dan saya bisa melihat pengecualian ditangkap dengan cara yang sama jika ditempatkan di dalam pernyataan menggunakan seperti contoh ShaneLS .

     try
     {
       using (var con = new SqlConnection(@"Data Source=..."))
       {
         var cad = "INSERT INTO table VALUES (@r1,@r2,@r3)";

         using (var insertCommand = new SqlCommand(cad, con))
         {
           insertCommand.Parameters.AddWithValue("@r1", atxt);
           insertCommand.Parameters.AddWithValue("@r2", btxt);
           insertCommand.Parameters.AddWithValue("@r3", ctxt);
           con.Open();
           insertCommand.ExecuteNonQuery();
         }
       }
     }
     catch (Exception ex)
     {
       MessageBox.Show("Error: " + ex.Message, "UsingTest", MessageBoxButtons.OK, MessageBoxIcon.Error);
     }

Tidak peduli di mana mencoba / menangkap ditempatkan, pengecualian akan ditangkap tanpa masalah.

Mengapa Begitu serius
sumber