没有EndExecuteNonQuery的BeginExecuteNonQuery

时间:2009-10-09 16:49:22

标签: c# ado.net

我有以下代码:

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 server运行一个存储过程,但我不想等待它,我甚至不关心它是否有效。那可能吗?谢谢你的阅读。

5 个答案:

答案 0 :(得分:24)

这不起作用,因为您在查询仍在运行时关闭连接。执行此操作的最佳方法是使用线程池,如下所示:

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。

答案 1 :(得分:11)

  1. 提交BeginExceuteNotQuery后无法关闭连接。它将中止执行。删除使用块。

  2. 要关闭连接,您必须知道通话何时完成。为此你必须调用EndExecuteNonQuery,通常来自回调:

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

    如果您想提交查询而不关心结果,请see Asynchronous T-SQL execution获取可靠模式,以确保即使客户端出现连接或崩溃也能执行。

答案 2 :(得分:5)

您应始终调用EndExecuteNonQuery()方法以防止泄漏。它可能现在可以工作,但谁知道在未来的.NET版本中会发生什么。一般规则始终遵循BeginExecute ...使用EndExecute ...

答案 3 :(得分:3)

我知道这是一个老帖子;根据我们最近(非常确定的)实施和测试添加我的2c:D

回答OP的问题:

  1. 如果不调用EndExecuteNonQuery,BeginExecuteNonQuery将执行该过程,但只要using子句处理了sql连接,操作就会被取消。因此,这不合理。
  2. 如果您通过使用委托调用BeginExecuteNonQuery,创建新线程等而不调用EndExecuteNonQuery,则可能会出现内存泄漏,具体取决于存储过程中发生的情况。 (稍后会详细介绍)。
  3. 调用存储过程而不是等待调用完成,就我的测试而言,是不可能的。无论多任务处理,某些地方都需要等待。
  4. 关于我们的解决方案:

    参考:BeginExecuteNonQuery - > BENQ,EndExecuteNonQuery - > EENQ

    使用案例

    我们有一个使用.Net TPL库的Windows服务(C#)。我们需要根据服务选择的附加请求,在运行时使用存储过程从一个数据库加载数据到另一个数据库。我们的存储过程有一个内部事务和使用try catch块的异常处理。

    首先尝试:

    我们第一次尝试在这个例子中实现了一个解决方案MS Solution,你会看到MS选择调用BENQ然后实现一个while循环来阻止执行,然后调用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我们已经对此进行了彻底的测试,并且没有遇到任何问题。

    快乐的编码!

答案 4 :(得分:0)

在这种情况下,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);
        }
    }
正如您所看到的,在命令完成后触发的lambda表达式(无论需要多长时间)都可以为您完成所有关闭。