[英]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 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. 你有几个选择。
SqlTransaction
in to GetAllCourses
and InsertCourseLookupRecord
so they can be part of the same transaction. SqlTransaction
传递给GetAllCourses
和InsertCourseLookupRecord
以便它们可以成为同一事务的一部分。 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. 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.