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 запускает хранимую процедуру, но я не хочу ждать ее, и мне все равно, работает ли она. Это возможно? Спасибо за чтение.
Ответы
Ответ 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);
}
}
Поскольку вы можете увидеть выражение лямбда, которое запускается после завершения команды (независимо от того, сколько времени потребуется), сделает все закрытие для вас.