将事务结果用作参数时发生超时错误

时间:2012-12-11 15:28:35

标签: c# concurrency ado.net transactions

我有以下使用SqlTransaction

的代码
string connectionString = ConfigurationManager.AppSettings["ConnectionString"];
using (SqlConnection connection = new SqlConnection(connectionString))
{
    connection.Open();
    SqlTransaction transaction = connection.BeginTransaction();

    int logID = HelperClass.InsertException(connection, 1, DateTime.Now, "Y", "test", "test", 1, 1, transaction);
    LogSearch logSearch = new LogSearch();
    logSearch.LogID = 258;

    Collection<Log> logs = HelperClass.GetLogs(logSearch, connectionString);
}

此代码抛出以下异常

  

超时已过期。操作完成之前经过的超时时间或服务器没有响应。

但是,如果我为LogID传递硬编码值,则没有例外。

问题

  1. 当我传递logID(来自InsertException()的结果)时,为什么会出现异常?
  2. 请解释为什么我将hard coded值作为LogID
  3. 传递时没有异常

    注意:InsertException()使用与SqlTransaction的连接,而GetLogs()使用没有任何事务的新连接

    更新的问题

    业务层代码不使用Transaction。我需要在上面显示的单元测试代码中调用业务层方法(用于集成测试)。即使业务层不使用事务,我们如何将事务应用于UT代码(用于集成测试)?从@jbl回答看来,似乎完全不可能在单元测试中使用事务。我们如何为UT代码申请交易。

    CODE

    public static class HelperClass
    {
        public static Collection<Log> GetLogs(LogSearch logSearch, string connectionString)
        {
            Collection<Log> logs = new Collection<Log>();
            using (SqlConnection connection = new SqlConnection(connectionString))
            {
                connection.Open();
                string commandText = "SELECT * FROM Application_EX WHERE application_ex_id = @application_ex_id";
                using (SqlCommand command = new SqlCommand(commandText, connection))
                {
                    command.CommandType = System.Data.CommandType.Text;
    
                    //Parameter value setting
                    command.Parameters.AddWithValue("@application_ex_id", logSearch.LogID);
    
                    using (SqlDataReader reader = command.ExecuteReader())
                    {
                        if (reader.HasRows)
                        {
                            while (reader.Read())
                            {
    
                            }
                        }
    
                    }
                }
            }
    
            return logs;
    
        }
    
        public static Int16 InsertException(SqlConnection connection, Int16 applicationID, DateTime createdDate, string isInternalLocationIndicator, string exceptionDescription, string operation, Int16 severityLevelNumber, Int16 exceptionTypeCode, SqlTransaction transaction)
        {
    
    
            Int16 newLogID = 0;
            string commandText = @"INSERT INTO Application_Ex
                                VALUES (@severity_level_nbr, @appl_service_id, @ex_internal_appl_ind, 
                                @application_ex_txt,@ex_location_txt,@create_ts,@application_ex_code);
                                SELECT SCOPE_IDENTITY() AS [LogIdentity];";
            using (SqlCommand command = new SqlCommand(commandText, connection, transaction))
            {
                command.CommandType = System.Data.CommandType.Text;
                command.Parameters.AddWithValue("@severity_level_nbr", severityLevelNumber);
                command.Parameters.AddWithValue("@appl_service_id", applicationID);
                command.Parameters.AddWithValue("@ex_internal_appl_ind", isInternalLocationIndicator);
                command.Parameters.AddWithValue("@application_ex_txt", exceptionDescription);
                command.Parameters.AddWithValue("@ex_location_txt", operation);
                command.Parameters.AddWithValue("@create_ts", createdDate);
                command.Parameters.AddWithValue("@application_ex_code", exceptionTypeCode);
    
                newLogID = Convert.ToInt16(command.ExecuteScalar(), CultureInfo.InvariantCulture);
            }
            return newLogID;
    
        }
    }
    

2 个答案:

答案 0 :(得分:7)

问题

  1. 当我传递logID(来自InsertException()的结果)时为什么会出现异常?
  2. 请解释为什么我将硬编码值作为LogID
  3. 传递时没有异常

    答复

    1. 使用事务插入新记录时,这意味着在您自己提交事务之前,最终不会提交新记录。在那之前,新记录是LOCKED,这意味着任何触及该新记录的查询都将暂停,直到发生超时。在您的情况下,在事务仍在运行时执行对GetLogs方法的调用,并且该方法搜索新插入的记录ID。由于该行被锁定,您的GetLogs调用将一直等到超时发生,这将导致超时异常。
    2. 在硬编码值的情况下,对GetLogs的调用将搜索具有相应id的现有记录。由于您正在搜索PK值,因此SQL不必搜索所有行,因为它是PK。因此,在单独的事务中找到并返回现有行,因为事务在它们触摸的数据中不重叠。
    3. 假设你的方法GetLogs正在另一列上搜索表,一个非pk列(例如application_message),那么必须读取整个表以找到一行(或多行),其中包含application_message的相应值。这将导致查询始终触及新插入的锁定行,然后还会使用硬编码(application_message)值获得超时异常。我添加的只是为了澄清锁定,当SQL确实或不需要触摸锁定的行时。

      希望这有帮助。

答案 1 :(得分:4)

我想那是因为HelperClass.GetLogs(logSearch, connectionString);在您的交易范围之外实例化了一个新连接:

您可以随意:

  • 让你的助手类接受持有事务而不是连接字符串的连接对象
  • 或将"SELECT * FROM Application_EX WHERE application_ex_id = @application_ex_id"替换为"SELECT * FROM Application_EX with (nolock) WHERE application_ex_id = @application_ex_id"

请注意,第二种情况有时会返回不正确的值,并且不会返回您当前在事务中插入的值

希望这会有所帮助