简体   繁体   English

SQL Timeout在不应该的时候过期

[英]SQL Timeout Expired When It Shouldn't

I am using the SqlConnection class and running into problems with command time outs expiring. 我正在使用SqlConnection类并遇到命令超时到期的问题。

First off, I am using the SqlCommand property to set a command timeout like so: 首先,我使用SqlCommand属性来设置命令超时,如下所示:

command.CommandTimeout = 300;

Also, I have ensured that the Execution Timeout setting is set to 0 to ensure that there should be no timeouts on the SQL Management side of things. 此外,我已确保将执行超时设置设置为0,以确保SQL管理方面不会有超时。

Here is my code: 这是我的代码:

using (SqlConnection conn = new SqlConnection(connection))
            {
                conn.Open();

                SqlCommand command = conn.CreateCommand();

                var transaction = conn.BeginTransaction("CourseLookupTransaction");

                command.Connection = conn;
                command.Transaction = transaction;
                command.CommandTimeout = 300;

                try
                {
                    command.CommandText = "TRUNCATE TABLE courses";
                    command.ExecuteNonQuery();

                    List<Course> courses = CourseHelper.GetAllCourses();

                    foreach (Course course in courses)
                    {
                        CourseHelper.InsertCourseLookupRecord(course);
                    }

                    transaction.Commit();
                }
                catch (Exception ex)
                {
                    transaction.Rollback();
                    Log.Error(string.Format("Unable to reload course lookup table: {0}", ex.Message));
                }
            }

I have set up logging and can verify exactly 30 seconds after firing off this function, I receive the following error message in my stack trace: 我已经设置了日志记录并且可以在启动此函数后正好验证30秒,我在堆栈跟踪中收到以下错误消息:

Timeout expired.  The timeout period elapsed prior to completion of the operation or the server is not responding.

In the interest of full disclosure: InsertCourseLookupRecord() found inside the above using statements foreach, is performing another query to the same table in the same database. 为了完全公开:在上面的using语句foreach中找到InsertCourseLookupRecord() ,正在对同一个数据库中的同一个表执行另一个查询。 Here is the query it is performing: 这是它正在执行的查询:

INSERT INTO courses(courseid, contentid, name, code, description, url, metakeywords, metadescription)
VALUES(@courseid, @contentid, @name, @code, @description, @url, @metakeywords, @metadescription)"

There is over 1400 records in this table. 此表中有超过1400条记录。

I will certify any individual(s) that helps me solve this as a most supreme grand wizard. 我将证明任何帮助我解决这个问题的个人是最高级的伟大巫师。

I believe what is happening is that you have a deadlock situation that is causing your query in the InsertCourseLookupRecord() function to fail. 我相信发生的事情是你有一个死锁情况导致你在InsertCourseLookupRecord()函数中的查询失败。 You are not passing your connection to InsertCourseLookupRecord() so I am assuming you are running that in a separate connection. 您没有将连接传递给InsertCourseLookupRecord()所以我假设您在单独的连接中运行它。 So what happens is: 那么会发生什么:

  • You started a transaction. 你开始了一项交易。
  • You truncate the table. 你截断表。
  • InsertCourseLookupRecord starts another connection and tries to insert data into that table, but the table is locked because your transaction isn't committed yet. InsertCourseLookupRecord启动另一个连接并尝试将数据插入该表,但该表已锁定,因为您的事务尚未提交。
  • The connection in the function InsertCourseLookupRecord() times out at the timeout value defined for that connection of 30 seconds. 函数InsertCourseLookupRecord()中的连接超时为该连接定义的超时值30秒。

You could change the function to accept the command object as a parameter and use it inside the function instead of creating a new connection. 您可以更改函数以接受命令对象作为参数,并在函数内部使用它而不是创建新连接。 This will then become part of the transaction and will all be committed together. 这将成为交易的一部分,并将全部一起提交。

To do this change your function definition to: 要执行此操作,请将函数定义更改为:

public static int InsertCourseLookupRecord(string course, SqlCommand cmd)

Take all the connection code out of the function because you're going to use the cmd object. 从函数中取出所有连接代码,因为您将使用cmd对象。 Then when you're ready to execute your query: 然后,当您准备好执行查询时:

myCommand.Parameters.Clear();  //need only if you're using command parameters
cmd.CommandText = "INSERT BLAH BLAH BLAH";
cmd.ExecuteNonQuery();

It will run under the same connection and transaction context. 它将在相同的连接和事务上下文中运行。

You call it like this in your using block: 你在你的使用块中这样称呼它:

CourseHelper.InsertCourseLookupRecord(course, command);

You could also just take the code in the InsertCourseLookupRecord and put it inside the for loop instead and then reuse the command object in your using block without needing to pass it to a function at all. 您也可以只使用InsertCourseLookupRecord中的代码并将其放在for循环中,然后在using块中重用命令对象,而无需将其传递给函数。

Because you are using two separate SqlConnection objects you are deadlocking your self due to the SqlTransaction you started in your outer code. 因为您使用了两个单独的SqlConnection对象,所以由于您在外部代码中启动的SqlTransaction而导致自我死锁。 The query in InsertCourseLookupReacord and maybe in GetAllCourses get blocked by the TRUNCATE TABLE courses call which has not yet been committed. InsertCourseLookupReacord的查询以及GetAllCourses的查询被尚未提交的TRUNCATE TABLE courses调用阻止。 They wait 300 seconds for the truncate to be committed then time out. 他们等待300秒,然后提交截断。

You have a few options. 你有几个选择。

  1. Pass the SqlConnection and SqlTransaction in to GetAllCourses and InsertCourseLookupRecord so they can be part of the same transaction. 将SqlConnection和SqlTransaction传递给GetAllCoursesInsertCourseLookupRecord以便它们可以成为同一事务的一部分。
  2. Use a "ambient transaction" by getting rid of the SqlTransaction and using a System.Transaction.TransactionScope instead. 通过删除SqlTransaction并使用System.Transaction.TransactionScope来使用“环境事务”。 This causes all connections that are opened to the server all share a transaction. 这会导致打开到服务器的所有连接都共享一个事务。 This can cause maintenance issues as depending on what the queries are doing it as it may need to invoke the Distributed Transaction Coordinator which may be disabled on some computers (from the looks of what you showed you would need the DTC as you have two open connections at the same time). 这可能会导致维护问题,具体取决于查询正在执行的操作,因为它可能需要调用可能在某些计算机上禁用的分布式事务处理协调器 (从您显示的内容看起来需要DTC,因为您有两个打开的连接同时)。

The best option is try to change your code to do option 1, but if you can't do option 2. 最好的选择是尝试更改代码以执行选项1,但如果您不能执行选项2。

Taken from the documentation : 取自文档

CommandTimeout has no effect when the command is executed against a context connection (a SqlConnection opened with "context connection=true" in the connection string) 对上下文连接执行命令时,CommandTimeout无效(在连接字符串中使用“context connection = true”打开SqlConnection)

Please review your connection string, thats the only possibility I can think of. 请检查您的连接字符串,这是我能想到的唯一可能性。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM