首页 > 解决方案 > SqlCommand.ExecuteNonQuery 抛出 Collection 被修改;枚举操作可能无法执行

问题描述

我有一个简单的方法来执行 SQL NON-QUERY 语句。这可以正常工作,没有任何问题,但是当有负载或 20 个线程同时调用此方法时,有时我会得到“集合已修改;枚举操作可能无法执行”。令人惊讶的是,我在此方法中没有任何枚举操作。

方法:

OpenConnection();
using (SqlCommand cmd = new SqlCommand(SQLQuery, Connection))
{
    cmd.CommandType = CommandType.Text;

    if (QueryParam.Count() > 0)
        cmd.Parameters.AddRange(QueryParam.ToArray());

    SqlParameter scopeParam = cmd.Parameters.AddWithValue("@ID", 0);
    scopeParam.Direction = ParameterDirection.Output;

    cmd.ExecuteNonQuery();
    uKey = (int)cmd.Parameters["@ID"].Value;
    cmd.Parameters.Clear();
}
CloseConnection()
return uKey;

例外:

Exception Found: Collection was modified; enumeration operation might not execute.

Full Exception: System.InvalidOperationException: Collection was modified; enumeration operation might not execute.
at System.Data.SqlClient.TdsParser.TdsExecuteRPC(SqlCommand cmd, _SqlRPC[] rpcArray, Int32 timeout, Boolean inSchema, SqlNotificationRequest notificationRequest, TdsParserStateObject stateObj, Boolean isCommandProc, Boolean sync, TaskCompletionSource`1 completion, Int32 startRpc, Int32 startParam)
at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Task& task, Boolean asyncWrite, Boolean inRetry, SqlDataReader ds, Boolean describeParameterEncryptionRequest)
at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, TaskCompletionSource`1 completion, Int32 timeout, Task& task, Boolean& usedCache, Boolean asyncWrite, Boolean inRetry)
at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource`1 completion, String methodName, Boolean sendToPipe, Int32 timeout, Boolean& usedCache, Boolean asyncWrite, Boolean inRetry)
at System.Data.SqlClient.SqlCommand.ExecuteNonQuery()

顺便提一下,我使用的是 .Net 4.0 和 SQL Server。任何帮助将不胜感激。

标签: c#sql.netsql-serverado.net

解决方案


本身显示的代码应该没问题;这里的症状表明另一个线程同时接触同一个Connection实例,所以:不要那样做。连接不是线程安全的。这里的关键赠品以粗体显示:

这可以正常工作,没有任何问题[可能本身意味着],但是当有负载或 20 个线程同时调用此方法时,有时我会得到“集合已修改;枚举操作可能无法执行”。

仅在有时且仅在并发负载下才会发生的事情:通常是并发性。建议:范围连接 - 每个线程,甚至每个方法,即

using (var conn = CreateOpenConnection())
using (var cmd = new SqlCommand(SQLQuery, conn))
{
    // ...
}

推荐阅读