Подтвердить что ты не робот

BeginExecuteNonQuery без EndExecuteNonQuery

У меня есть следующий код:

using (SqlConnection sqlConnection = new SqlConnection("blahblah;Asynchronous Processing=true;")
{
    using (SqlCommand command = new SqlCommand("someProcedureName", sqlConnection))
    {
        sqlConnection.Open();

        command.CommandType = CommandType.StoredProcedure;
        command.Parameters.AddWithValue("@param1", param1);

        command.BeginExecuteNonQuery();
    }
}

Я никогда не вызываю EndExecuteNonQuery.

Два вопроса: сначала будет этот блок из-за использования утверждений или по любой другой причине? Во-вторых, что-нибудь сломает? Как утечки или проблемы с подключением? Я просто хочу сказать, что сервер sql запускает хранимую процедуру, но я не хочу ждать ее, и мне все равно, работает ли она. Это возможно? Спасибо за чтение.

4b9b3361

Ответ 1

Это не будет работать, потому что вы закрываете соединение, пока запрос все еще запущен. Лучший способ сделать это - использовать threadpool, например:

ThreadPool.QueueUserWorkItem(delegate {
    using (SqlConnection sqlConnection = new SqlConnection("blahblah;Asynchronous Processing=true;") {
        using (SqlCommand command = new SqlCommand("someProcedureName", sqlConnection)) {
            sqlConnection.Open();

            command.CommandType = CommandType.StoredProcedure;
            command.Parameters.AddWithValue("@param1", param1);

            command.ExecuteNonQuery();
        }
    }
});

В общем, когда вы вызываете Begin_Whatever_, вы обычно должны вызывать End_Whatever_ или вы будете утечка памяти. Большим исключением из этого правила является Control.BeginInvoke.

Ответ 2

  • Вы не можете закрыть соединение после отправки BeginExceuteNotQuery. Он прервет выполнение. Удалите используемый блок.

  • Чтобы закрыть соединение, вы должны знать, когда вызов завершился. Для этого вы должны вызвать EndExecuteNonQuery, обычно из обратного вызова:

.

command.BeginExecuteNonQuery(delegate (IAsyncResult ar) {
   try { command.EndExecuteNonQuery(ar); }
   catch(Exception e) { /* log exception e */ }
   finally { sqlConnection.Dispose(); }
   }, null);

Если вы хотите отправить запрос и не заботитесь о результатах, см. Асинхронное выполнение T-SQL для надежного шаблона, который обеспечивает выполнение, даже если клиент diconnect или сбой.

Ответ 3

Вы всегда должны вызывать метод EndExecuteNonQuery() для предотвращения утечек. Теперь он может работать, но кто знает, что произойдет в будущих версиях .NET. Общее правило всегда следует за BeginExecute... с помощью EndExecute...

Ответ 4

Я знаю, что это старый пост; просто добавив мой 2c на основе нашей недавней (очень убедительной) реализации и тестирования: D

Чтобы ответить на вопросы OP:

  • Если вы не вызываете EndExecuteNonQuery, BeginExecuteNonQuery будет выполнять эту процедуру, но операция будет отменена, как только предложение use будет использовать ваше соединение sql. Следовательно, это не правдоподобно.
  • Если вы вызываете BeginExecuteNonQuery с помощью делегата, создаете новый поток и т.д., и вы не вызываете EndExecuteNonQuery, скорее всего, вы можете получить утечку памяти в зависимости от того, что происходит в вашей хранимой процедуре. (Подробнее об этом позже).
  • Вызов хранимой процедуры и не дожидаясь завершения звонка, пока я не прошел тестирование, невозможно. Независимо от многозадачности, что-то где-то придется ждать.

На наше решение:

Refs: BeginExecuteNonQuery → BENQ, EndExecuteNonQuery → EENQ

Случай использования:

У нас есть служба Windows (С#), которая использует библиотеку .Net TPL. Нам нужно было загружать данные с помощью хранимой процедуры из одной базы данных в другую во время выполнения, на основе запроса дополнительной информации, который получает служба. Наша хранимая процедура имела внутреннюю транзакцию и обработку исключений с блоками try catch.

Сначала попробуйте:

Для нашей первой попытки мы внедрили решение, найденное здесь MS Solution в этом примере вы увидите, что MS выбирает вызов BENQ, а затем реализует некоторое время цикл, чтобы заблокировать выполнение, а затем вызывает EENQ. Это решение было в основном реализовано, если вам не нужен метод обратного вызова. Проблема с этим решением заключается в том, что только BENQ не знает о тайм-аутах соединения sql. EENQ отключится. Поэтому для долгого запроса (который, мы надеемся, причина, по которой вы используете BENQ) вы застрянете в то время, и как только операция завершится, и вы вызовете EENQ, вы получите соединение с тайм-аутом sql.

Вторая попытка:

Для нашей второй попытки мы подумали, что давайте позвоним BENQ, а затем добавим некоторое время, чтобы мы не закрывали наше соединение sql и никогда не вызывали EENQ. Это сработало, пока исключение не было брошено в нашей хранимой процедуре. Поскольку мы никогда не называли EENQ, операция никогда не была завершена, и исключение никогда не подпрыгивало до нашего кода. Следовательно, мы навсегда застряли в утечке цикла/потока/памяти.

Третий пример: (Решение)

Для нашей третьей попытки мы решили позвонить BENQ, а затем сразу после вызова EENQ. Случилось так, что EENQ эффективно блокировал выполнение в потоке до завершения операции. Когда в хранимой процедуре произошло исключение, она была поймана. Когда запрос длится долго, EENQ не выбрасывает исключение тайм-аута, и во всех случаях наш объект соединения sql был удален, а также наш поток.

Вот некоторые отрывки нашего кода:

Здесь мы открываем новый поток для метода, который вызывает хранимую процедуру.

//Call the load data stored procedure. As this stored procedure can run longer we start it in its own thread.
Task.Factory.StartNew(() => ClassName.MethodName(Parameters));

Это код внутри метода, который мы используем для вызова хранимой процедуры.

//Because this is a long running stored procedure, we start is up in a new thread.
using (SqlConnection conn = new   SqlConnection(ConfigurationManager.ConnectionStrings[ConfigurationManager.AppSettings["ConnectionStringName"]].ConnectionString))
{
    try
    {
        //Create a new instance SqlCommand.
        SqlCommand command = new SqlCommand(ConfigurationManager.AppSettings["StoredProcedureName"], conn);

        //Set the command type as stored procedure.
        command.CommandType = CommandType.StoredProcedure;

        //Create input parameters.
        command.Parameters.Add(CreateInputParam("@Param1", SqlDbType.BigInt, Param1));
        command.Parameters.Add(CreateInputParam("@Param2", SqlDbType.BigInt, Param3));
        command.Parameters.Add(CreateInputParam("@Param3", SqlDbType.BigInt, Param3));

        //Open up the sql connection.
        conn.Open();

        //Create a new instance of type IAsyncResult and call the sp   asynchronously.
        IAsyncResult result = command.BeginExecuteNonQuery();

         //When the process has completed, we end the execution of the sp.
        command.EndExecuteNonQuery(result);
    }
    catch (Exception err)
    {
        //Write to the log.
    }
}

Я надеюсь, что этот ответ спасет кого-то головную боль: D Мы тщательно протестировали это и не испытывали никаких проблем.

Счастливое кодирование!

Ответ 5

В этом случае операторы using не нужны, потому что вы должны вручную закрыть их самостоятельно, вместо того, чтобы позволить синтаксическому сахару распоряжаться им (т.е. на }). Это должно быть так просто, как это сделать, чтобы у вас не было утечек.   

    using (SqlConnection sqlConnection = new SqlConnection("blahblah;Asynchronous Processing=true;")
    {
        using (SqlCommand command = new SqlCommand("someProcedureName", sqlConnection))
        {
            sqlConnection.Open();
            command.CommandType = CommandType.StoredProcedure;
            command.Parameters.AddWithValue("@param1", param1);
            command.BeginExecuteNonQuery((ar) =>
            {
                var cmd = (SqlCommand)ar.AsyncState;
                cmd.EndExecuteNonQuery(ar);
                cmd.Connection.Close();
            }, command);
        }
    }
Поскольку вы можете увидеть выражение лямбда, которое запускается после завершения команды (независимо от того, сколько времени потребуется), сделает все закрытие для вас.