Моя переменная SqlCommand перегружена?Это устало? - PullRequest
3 голосов
/ 27 марта 2012

В моем тестовом проекте у меня есть статический класс 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 содержал только ОДИН параметр, добавлялся второй, и когда я принудительно заставлял первый cmd.Parameters.Добавить строку для повторного выполнения, количество параметров в списке упало до 0. Вот что побудило меня попробовать метод уровня SqlCommand ... потому что у меня была сумасшедшая идея, что мой cmd устал ... Представьте, что я шок, когда я, видимо, оказался прав!

Редактировать: Здесь я не перерабатываю никакие объекты - просто ссылку на переменную (статическая SqlCommand на уровне класса). Приношу свои извинения за раннюю путаницу в моей формулировке вопроса.

Ответы [ 4 ]

3 голосов
/ 27 марта 2012

используйте одну команду на запрос и вызывайте dispose (или, что еще лучше, добавьте оператор using). Вы не хотите «повторно использовать» компоненты ado.net.

2 голосов
/ 27 марта 2012

Убедитесь, что вы не установили DataReader на CommandBehavior.CloseConnection, так как вы упомянули, что повторно используете соединение для инициализации теста.

Кроме того,DataReader требует ресурсов, поэтому утилизируйте

2 голосов
/ 27 марта 2012

Большой Редактировать:

От: 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();
}
0 голосов
/ 27 марта 2012

Вам действительно нужно создавать новый объект подключения после каждой попытки?

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