Моя переменная SqlCommand перегружена? Это устало?
В моем тестовом проекте у меня есть статический класс FixtureSetup, который я использую для настройки данных интеграционного тестирования на валидацию.
Я использую одну и ту же переменную SqlCommand и SqlParameter (не сам объект) внутри этого класса, многократно, снова и снова используя одни и те же ссылки на переменные, каждый раз назначая новые объекты SqlCommand и SqlParameter. Само мое соединение создается один раз и передается в методы, выполняющие настройку, поэтому каждая настройка использует свою собственную отдельную ссылку на соединение, и хотя одно и то же соединение используется несколько раз, оно всегда в линейной последовательности.
В одном из таких методов я столкнулся с очень странной ситуацией, когда моя переменная SqlCommand, похоже, просто устала.
cmd = new SqlCommand("INSERT INTO Subscription (User_ID, Name, Active) VALUES (@User_ID, @Name, @Active)", conn);
parameter = new SqlParameter("@User_ID", TestUserID); cmd.Parameters.Add(parameter);
parameter = new SqlParameter("@Name", "TestSubscription"); cmd.Parameters.Add(parameter);
parameter = new SqlParameter("@Active", true); cmd.Parameters.Add(parameter);
cmd.ExecuteNonQuery();
cmd = new SqlCommand("SELECT Subscription_ID FROM [Subscription] WHERE Name = 'TestSubscription'", conn);
parameter = new SqlParameter("@User_ID", TestUserID);
cmd.Parameters.Add(parameter);
using (dr = cmd.ExecuteReader())
{
while (dr.Read())
{
TestSubscriptionID = dr.GetInt32(dr.GetOrdinal("Subscription_ID"));
}
}
cmd = new SqlCommand("INSERT INTO SubscriptionCompany (Subscription_ID, Company_ID) VALUES (@Subscription_ID, @Company_ID)", conn);
parameter = new SqlParameter("@Subscription_ID", TestSubscriptionID); cmd.Parameters.Add(parameter);
parameter = new SqlParameter("@Company_ID", KnownCompanyId); cmd.Parameters.Add(parameter);
cmd.ExecuteNonQuery();
Выше, в последней показанной строке, выполняя то же самое, что я сделал буквально в десятках других мест (вставьте данные, прочитайте столбец идентификатора и захватите их), я получу следующее:
SetUp: System.InvalidOperationException: ExecuteNonQuery требует открытого и доступного соединения. Текущее состояние соединения закрыто. в System.Data.SqlClient.SqlConnection.GetOpenConnection(метод String)
НО - замените cmd новой переменной myCmd, и все работает плавно!
SqlCommand myCmd;
myCmd = new SqlCommand("INSERT INTO Subscription (User_ID, Name, Active) VALUES (@User_ID, @Name, @Active)", conn);
parameter = new SqlParameter("@User_ID", TestUserID); myCmd.Parameters.Add(parameter);
parameter = new SqlParameter("@Name", "TestSubscription"); myCmd.Parameters.Add(parameter);
parameter = new SqlParameter("@Active", true); myCmd.Parameters.Add(parameter);
myCmd.ExecuteNonQuery();
myCmd = new SqlCommand("SELECT Subscription_ID FROM [Subscription] WHERE Name = 'TestSubscription'", conn);
parameter = new SqlParameter("@User_ID", TestUserID);
myCmd.Parameters.Add(parameter);
using (dr = myCmd.ExecuteReader())
{
while (dr.Read())
{
TestSubscriptionID = dr.GetInt32(dr.GetOrdinal("Subscription_ID"));
}
}
myCmd = new SqlCommand("INSERT INTO SubscriptionCompany (Subscription_ID, Company_ID) VALUES (@Subscription_ID, @Company_ID)", conn);
parameter = new SqlParameter("@Subscription_ID", TestSubscriptionID); myCmd.Parameters.Add(parameter);
parameter = new SqlParameter("@Company_ID", KnownCompanyId); myCmd.Parameters.Add(parameter);
myCmd.ExecuteNonQuery();
Какого черта здесь происходит? Моя команда var просто устала???
Что подсказало мне "исправить", так это то, что я заметил в своей трассировке, что в моем блоке "чтение идентификатора" мой блок cmd.Parameters содержал только ОДИН параметр, добавлялся 2-й, и когда я запускал первый cmd.Parameters..Добавьте строку для повторного выполнения, количество параметров в списке упало до 0. Вот что побудило меня попробовать метод уровня SqlCommand... потому что у меня была сумасшедшая идея, что мой cmd устал... Представьте, что я шокирован, когда я по-видимому, оказался прав!
Редактировать: я не перерабатываю никаких объектов здесь - просто ссылка на переменную (статический SqlCommand на уровне класса). Приношу свои извинения за раннюю путаницу в моей формулировке вопроса.
4 ответа
Большой Редактировать:
От: http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqldatareader.close.aspx
Вы должны явно вызывать метод Close, когда вы используете SqlDataReader, чтобы использовать связанный SqlConnection для любых других целей.
Метод Close заполняет значения для выходных параметров, возвращаемых значений и RecordsActed, увеличивая время, необходимое для закрытия SqlDataReader, который использовался для обработки большого или сложного запроса. Если возвращаемые значения и количество записей, затронутых запросом, не являются значительными, время, необходимое для закрытия SqlDataReader, можно уменьшить, вызвав метод Cancel связанного объекта SqlCommand перед вызовом метода Close.
так что постарайтесь:
using (dr = cmd.ExecuteReader())
{
while (dr.Read())
{
TestSubscriptionID = dr.GetInt32(dr.GetOrdinal("Subscription_ID"));
}
dr.Close();
}
Используйте одну команду на запрос и вызовите dispose (или еще лучше, оберните в оператор using). Вы не хотите "повторно использовать" компоненты ado.net.
Убедитесь, что вы не установили DataReader
в CommandBehavior.CloseConnection
так как вы упомянули, что повторно используете соединение для инициализации теста.
Так же DataReader
действительно берет ресурсы, так что утилизируйте
Вам действительно нужно создавать новый объект подключения после каждой попытки?
myCmd = new SqlCommand(...)
//your code
myCmd = new SqlCommand(...)
//etc
Вы можете просто сказать:
myCmd.CommandText = "INSERT INTO SubscriptionCompany (Subscription_ID, Company_ID) VALUES (@Subscription_ID, @Company_ID)";
так что вы можете повторно использовать ваш командный объект. Кроме того, вы можете сбросить параметры после каждого звонка. Просто позвони myCmd.Parameters.Clear()
,
Кроме того, убедитесь, что вы заверните SqlCommand
в using
заявление, чтобы они были должным образом убраны.
using (SqlCommand cmd = new SqlCommand())
{
cmd.CommandText = "some proc"
cmd.Connection = conn;
//set params, get data, etc
cmd.CommandText = "another proc"
cmd.Parameters.Clear();
//set params, get date, etc.
}