1

我有以下使用 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传递时没有异常

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

更新的问题

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

代码

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;

    }
}
4

2 回答 2

7

问题

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

答案

  1. 当使用事务插入新记录时,这意味着在您自己提交事务之前不会最终提交新记录。在此之前,新记录被锁定,这意味着任何触及该新记录的查询都将被挂起,直到发生超时。在您的情况下,在事务仍在运行时执行对方法 GetLogs 的调用,并且该方法搜索新插入的记录 ID。由于该行被锁定,您的 GetLogs 调用将一直等到发生超时,这会导致超时异常。
  2. 在硬编码值的情况下,对 GetLogs 的调用将搜索具有相应 id 的现有记录。由于您正在搜索 PK 值,因此 SQL 不必搜索所有行,因为它是 PK。因此,现有行被找到并返回,所有这些都在一个单独的事务中,因为事务在它们接触的数据中没有重叠。

假设您的方法 GetLogs 正在搜索另一列上的表,即非 pk 列(例如 application_message),则必须读取整个表才能找到具有 application_message 对应值的行(或行)。这将导致查询始终触及新插入的锁定行,然后使用硬编码 (application_message) 值,您将获得超时异常。我添加这个只是为了澄清锁定,以及 SQL 何时需要或不需要触摸锁定的行。

希望这可以帮助。

于 2013-01-09T12:55:09.217 回答
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"

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

希望这会有所帮助

于 2012-12-11T15:42:48.683 回答