2008-10-30 4 views
28

Я просто смотрю на использование заявления, я всегда знал, что он делает, но до сих пор не пытались использовать его, я придумал код ниже:C# ошибки при использовании личных данных поймать

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

Это кажется чтобы работать, но есть ли какой-то момент в этом, поскольку, насколько я могу судить, мне все равно нужно вложить это в блок catch try, чтобы поймать непредвиденные ошибки, например sql-сервер. Я что-то упускаю?

Насколько я могу сейчас видеть это просто останавливает меня закрытие и удаление CMD, но будет больше строк кода из-за попытки поймать все еще необходимо.

ответ

18

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

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

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

4

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

+2

Я вижу, что вы говорите, но я не вижу преимущества над блоком try catch, заключая с закрытыми и распоряжающимися утверждениями. – PeteT 2008-10-30 01:05:45

+2

Ресурсы не могут находиться вне вида сборщика мусора. По-прежнему полезно очищать их как можно скорее, а не ждать GC. – 2010-03-24 22:38:37

+1

дело не в сборщике мусора. – 2010-08-12 19:20:47

2

Да, вам все равно придется ловить исключения. Преимущество используемого блока заключается в добавлении области к вашему коду. Вы говорите: «Внутри этого блока кода делайте что-то, и когда оно доходит до конца, закрывайте и удаляйте ресурсы»

Это совсем не обязательно, но оно определяет ваши намерения кому-либо еще, используя ваш код , а также помогает не оставлять связи и т. д. открытыми по ошибке.

57

При выполнении работы IO I код ожидать исключение.

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

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

Редакция 2: Можно утверждать, что используемый блок может обернуть try/catch в этой ситуации, и это полностью действительный и функционально эквивалентный. Это действительно сводится к предпочтению. Вы хотите избежать дополнительного гнездования за счет обработки вашего собственного распоряжения? Или вы несете дополнительное гнездование для автоматического удаления. Я чувствую, что первый чище, поэтому я так делаю. Однако я не переписываю последнее, если найду его в базе кода, в которой я работаю.

Редактировать 3: Я действительно очень хочу, чтобы MS создала более явную версию использования(), которая сделала его более интуитивно понятным, что на самом деле происходило и в этом случае было больше гибкости. Рассмотрим следующую, воображаемую код:

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

А, используя оператор создает только попытка/наконец Dispose() вызывает в конце концов. Почему бы не дать разработчику единый способ удаления и обработки исключений?

1

Оператор using фактически преобразуется в блок try/finally компилятором, в котором удаляется параметр используемого блока, если он реализует интерфейс IDisposable. Помимо обеспечения того, чтобы указанные объекты были надлежащим образом удалены, когда они выпадают из области видимости, на самом деле нет ошибки, полученной при использовании этой конструкции.

Как указано выше, вы должны убедиться, что объекты SqlConnection и SqlCommand находятся в правильном порядке. Укладка обоих в один блок используется немного грязно и может не делать то, что вы думаете.

Также обратите внимание на использование блока try/catch как логики. Это запах кода, который мой нос вызывает особую неприязнь, и часто используется новичками или теми из нас, которые спешат встретить крайний срок.

1

FYI, в этом конкретном примере, поскольку вы используете соединение ADO.net и объект Command, имейте в виду, что оператор using просто выполняет команду Command.Dispose и Connection.Dispose(), которые фактически не закрываются соединение, но просто возвращает его обратно в пул соединений ADO.net, который будет повторно использован при следующем подключении.open ... что хорошо, и абсолютно правильная вещь, чтобы сделать, bc, если вы этого не сделаете, соединение останется до тех пор, пока сборщик мусора не отпустит его обратно в пул, что может быть не до тех пор, пока не будет получено множество других запросов на соединение, которые в противном случае были бы вынуждены создавать новые соединения, даже если есть неиспользованный, ожидающий сбора мусора.

5

Разрабатывая то, что сказал Крис Балланс, спецификация C# (ECMA-334 версия 4), раздел 15.13, гласит: «Оператор использования транслируется на три части: сбор, использование и удаление. Использование ресурса неявно заключено в try, который включает в себя предложение finally. Это предложение finally предоставляет ресурс. Если нулевой ресурс получен, то вызов Dispose не производится, и исключение не генерируется ».

Описание близко к 2 страницам - стоит прочитать.

По моему опыту, SqlConnection/SqlCommand может генерировать ошибки во многих отношениях, которые вам почти необходимы для обработки исключений, которые вызывают больше, чем обрабатывают ожидаемое поведение. Я не уверен, что мне бы понадобилось предложение use здесь, так как я хотел бы иметь возможность обрабатывать случай с нулевым ресурсом.

2

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

Независимо от того, какой метод «Dispose» будет вызван на объект в блоке «using». Если вы поместите оператор return или выбросите ошибку, вызывается «Dispose».

Пример:

Я сделал класс под названием "MyDisposable", и он реализует IDisposable и просто делает Console.Write. It всегда пишет в консоль даже во всех этих сценариях:

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

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

Если ваш код выглядит следующим образом:

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

Тогда я бы реорганизовать его использовать попробовать .. поймать .. наконец-то вместо ,

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

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

0

Если вызывающая сторона вашей функции несет ответственность за любые исключения, оператор using является хорошим способом обеспечения очистки ресурсов независимо от результата.

Он позволяет размещать код обработки исключений на границах уровня/сборки и помогает предотвратить слишком заторможенность других функций.

Конечно, это действительно зависит от типов исключений, создаваемых вашим кодом. Иногда вам следует использовать try-catch-finally, а не инструкцию using. Моя привычка состоит в том, чтобы всегда начинать с использования инструкции для IDisposables (или иметь классы, которые содержат IDisposables, также реализуют интерфейс) и добавлять try-catch-finally по мере необходимости.

14

Там не может быть никаких преимуществ использования using заявления, в этом случае, если вы собираетесь иметь блок try/catch/finally в любом случае. Как вы знаете, оператор using представляет собой синтаксический сахар для try/finally, который имеет объект IDisposable. Если вы все равно будете иметь свой собственный try/finally, вы, безусловно, можете сами сделать Dispose.

Это действительно в основном сводится к стилю - ваша команда может быть более комфортной с заявлениями using или using заявления могут сделать код более чистым.

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

0

Таким образом, в принципе «использование» является таким же, как «Try/catch/finally», только более гибким для обработки ошибок.

0

Minor поправка к примеру: SqlDataAdapter также должен быть создан в using заявлении:

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

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

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 ... 

} 

еще не будет никакой необходимости писать код для утилизации MyDisposableObj б/c было бы обработано using ...

Как это сделать?

1

Я бы принял решение о том, когда и когда не использовать инструкцию использования, зависящую от ресурса, с которым я имею дело. В случае ограниченного ресурса, такого как ODBC-соединение, я бы предпочел использовать T/C/F, чтобы я мог регистрировать осмысленные ошибки в той точке, в которой они произошли. Предотвращение ошибок драйвера базы данных возвращается к клиенту и потенциально может быть потеряно на более высоком уровне. Обертка исключений является оптимальной.

T/C/F дает вам душевное спокойствие, что ресурс обрабатывается так, как вы хотите. Как уже отмечалось, оператор using не предоставляет обработку исключений, он просто гарантирует, что ресурс разрушен. Обработка исключений - это недооцененная и недооцененная языковая структура, которая часто является разницей между успехом и неудачей решения.

0

Во-первых, ваш пример кода должен быть:

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

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

Если вам нужно обрабатывать исключения в конструкции соединения и команды (а также при их использовании), да, вы должны обернуть всю вещь в Try/уловом:

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

Но вам не нужно обрабатывать очистку conn или cmd; это уже сделано для вас.

Контраст с теми же вещами без 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 

Я знаю, который я предпочел бы писать. :-)

Смежные вопросы