У меня есть два вопроса.
1) Необходимо ли всегда использовать оператор использования на соединении? Так, я использовал бы его на соединении и затем другом на читателе в рамках соединения? Таким образом, я использовал бы два оператора использования.
2) Позволяет говорят, что Вы используете оператор использования на соединении и также читателе, возвращаемом на соединении. Таким образом, у Вас есть два оператора использования. Это создает две Попытки {} Наконец {} блоки или всего один?
Спасибо!
Вы всегда должны использовать оператор using
, когда объект реализует IDisposable
. Это включает в себя соединения.
Будет создано два вложенных блока try {} finally {}
.
Будьте осторожны здесь. Вы должны всегда иметь оператор using для любого локального объекта, который реализует IDisposable. Это включает не только связи и читателей, но и команду. Но иногда это может быть сложно, именно где используется инструкция using. Если вы не будете осторожны, это может вызвать проблемы. Например, в коде, который следует за оператором using, программа чтения закроет еще до того, как вы сможете его использовать:
DataReader MyQuery()
{
string sql="some query";
using (var cn = new SqlConnection("connection string"))
using (var cmd = new SqlCommand(sql, cn))
{
cn.Open();
using (var rdr = cmd.ExecuteReader())
{
return rdr;
}
}
}
Вместо этого у вас есть четыре варианта. Один из них - дождаться создания блока using, пока вы не вызовете функцию:
DataReader MyQuery()
{
string sql="some query";
using (var cn = new SqlConnection("connection string"))
using (var cmd = new SqlCommand(sql, cn))
{
cn.Open();
return cmd.ExecuteReader();
}
}
using (var rdr = MyQuery())
{
while (rdr.Read())
{
//...
}
}
Конечно, вы все равно должны быть осторожны с вашим соединением там, и это означает, что не забудьте написать блок using везде, где вы используете функцию.
Второй вариант - просто обрабатывать результаты запроса в самом методе, но это нарушает отделение уровня данных от остальной части программы.Третий вариант заключается в том, что ваша функция MyQuery () принимает аргумент типа Action, который вы можете вызывать внутри цикла while (rdr.Read ()), но это просто неудобно.
Обычно я предпочитаю четвертый вариант: превратить средство чтения данных в IEnumerable, например:
IEnumerable<IDataRecord> MyQuery()
{
string sql="some query";
using (var cn = new SqlConnection("connection string"))
using (var cmd = new SqlCommand(sql, cn))
{
cn.Open();
using (var rdr = cmd.ExecuteReader())
{
while (rdr.Read())
yield return rdr;
}
}
}
Теперь все будет правильно закрыто, а код, который его обрабатывает, находится в одном месте. Вы также получаете приятный бонус: результаты вашего запроса будут хорошо работать с любым из операторов linq.
Наконец, кое-что новое, с чем я играю в следующий раз, когда я смогу создать полностью новый проект, который объединяет IEnumerable с передачей аргумента делегата:
//part of the data layer
private static IEnumerable<IDataRecord> Retrieve(string sql, Action<SqlParameterCollection> addParameters)
{
//DL.ConnectionString is a private static property in the data layer
// depending on the project needs, it can be implementing to read from a config file or elsewhere
using (var cn = new SqlConnection(DL.ConnectionString))
using (var cmd = new SqlCommand(sql, cn))
{
addParameters(cmd.Parameters);
cn.Open();
using (var rdr = cmd.ExecuteReader())
{
while (rdr.Read())
yield return rdr;
}
}
}
А затем я использую его на уровне данных, например это:
public IEnumerable<IDataRecord> GetFooChildrenByParentID(int ParentID)
{
//I could easily use a stored procedure name instead, and provide overloads for commandtypes.
return Retrieve(
"SELECT c.*
FROM [ParentTable] p
INNER JOIN [ChildTable] c ON c.ParentID = f.ID
WHERE f.ID= @ParentID", p =>
{
p.Add("@ParentID", SqlDbType.Int).Value = ParentID;
}
);
}
1) Следует ли всегда использовать оператор using в соединении? Итак, я бы использовал его для соединения, а затем еще один на считывателе в соединении ? Поэтому я бы использовал два оператора using .
Да, потому что они реализуют IDisposable
. И не забудьте также использовать оператор using
в команде:
using (DbConnection connection = GetConnection())
using (DbCommand command = connection.CreateCommand())
{
command.CommandText = "SELECT FOO, BAR FROM BAZ";
connection.Open();
using (DbDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
....
}
}
}
2) Допустим, вы используете оператор using в соединении, а также возвращаемый читатель на соединении . Итак, у вас есть два оператора using . Создает ли он два блока Try {} finally {} или только один?
Каждый using
оператор создает свой собственный блок try / finally
Возможно, эта статья будет вам интересна: Как реализовать IDisposable и Finalizers: 3 простых правила
Чтобы ответить каждому:
1) Да, лучше всего избавиться от обоих как можно скорее.
2) using ()
создаст два блока, обернутых друг в друга в одном и том же порядке. Сначала он удалит внутренний объект (считыватель), а затем удалит объект из внешнего, используя (соединение).
Особый пункт по 1). Вам нужно специально избегать этого метода, когда соединение используется в асинхронных методах ADO.NET - например, BeginExecuteReader, потому что, скорее всего, вы выпадете из области видимости и попытаетесь избавиться от соединение, пока выполняется асинхронная операция. Это похоже на случай, когда вы используете переменные класса, а не локальные переменные. Часто ссылка на соединение сохраняется в классе, используемом как «блок управления» для асинхронной операции.