В блоке "using" есть SqlConnection, закрытое по возврату или исключению?
Первый вопрос:
Скажем, у меня
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();
}
Закрывается ли соединение? Потому что технически мы никогда не доходим до последнего }
, пока мы return
до него.
Второй вопрос:
На этот раз я:
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*/ }
Теперь, скажем где-то в try
, мы получаем ошибку, и она попадает. Закрыта ли связь? Поскольку мы снова пропустим остальную часть кода в try
и перейдем непосредственно к оператору catch
.
Думаю ли я слишком линейно, как работает using
? т.е. вызывает ли Dispose()
вызов, когда мы оставляем область using
?
Ответы
Ответ 1
В любом случае, когда используется используемый блок (либо путем успешного завершения, либо по ошибке), он закрывается.
Хотя я думаю, что это было бы лучше, так как это намного легче увидеть, что произойдет, даже для нового программиста по обслуживанию, который будет поддерживать его позже:
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*/
}
}
Ответ 2
Да на оба вопроса. Оператор using скомпилирован в блок try/finally
using (SqlConnection connection = new SqlConnection(connectionString))
{
}
совпадает с
SqlConnection connection = null;
try
{
connection = new SqlConnection(connectionString);
}
finally
{
if(connection != null)
((IDisposable)connection).Dispose();
}
Изменить: привязка роли к одноразовому
http://msdn.microsoft.com/en-us/library/yh598w02.aspx
Ответ 3
Вот мой шаблон. Все, что вам нужно для выбора данных с SQL-сервера. Соединение закрыто и расположено, и ошибки в соединении и выполнении пойманы.
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();
}
}
* Пересмотрено: 2015-11-09 *
Как было предложено NickG; Если слишком много брекетов раздражает вас, отформатируйте вот так...
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();
}
И снова, если вы работаете в EA или DayBreak, вы можете просто отказаться от любых перерывов, потому что они предназначены только для людей, которые должны вернуться и посмотреть на свой код позже, и кто действительно заботится? Я прав? Я имею в виду 1 строку вместо 23 означает, что я лучший программист, не так ли?
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(); }
Фу... Хорошо. Я получил это из своей системы и немного занят собой. Продолжайте.
Ответ 4
Dispose просто вызывается, когда вы покидаете область использования. Цель "использования" - предоставить разработчикам гарантированный способ убедиться, что ресурсы удалены.
От MSDN:
Оператор using может быть завершен либо при достижении конца инструкции using, либо при вызове исключения, и управление покидает блок операторов до конца инструкции.
Ответ 5
Using
генерирует попытку/окончательно вокруг выделенного объекта и вызывает для вас Dispose()
.
Это избавляет вас от необходимости вручную создавать блок try/finally и вызывать Dispose()
Ответ 6
В вашем первом примере компилятор С# фактически преобразует оператор using в следующее:
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();
}
Наконец, операторы всегда будут вызваны до возвращения функции, поэтому соединение всегда будет закрыто/расположено.
Итак, в вашем втором примере код будет скомпилирован следующим образом:
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)
{
}
Исключение будет найдено в последнем объявлении, а соединение закрыто. Исключение не будет видно из предложения внешнего catch.
Ответ 7
Я написал два оператора using внутри блока try/catch, и я видел, что исключение было поймано таким же образом, если оно помещено внутри внутреннего оператора using так же, как 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);
}
Независимо от места размещения try/catch исключение будет устранено без проблем.