无法执行事务操作,因为有待处理的请求在工作
The transaction operation cannot be performed because there are pending requests working
背景
我有一些代码可以打开 sql 连接,开始事务并对数据库执行一些操作。此代码从数据库创建一个对象(出队),获取一些值并将其保存回来。整个操作需要在事务中进行。所有代码在没有事务的情况下都能完美运行。
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
var transaction = connection.BeginTransaction();
try
{
var myObject = foo.Dequeue(connection, transaction);
var url = myObj.GetFilePathUri(connection, transaction);
//some other code that sets object values
myObj.SaveMessage(connection, transaction);
transaction.Commit(); //error here
}
catch(Exception ex)
{
transaction.Rollback();
//logging
}
finally
{
//cleanup code
}
}
dequeue方法代码
public foo Dequeue(SqlConnection connection, SqlTransaction transaction)
{
using (var command = new SqlCommand(DEQUEUE_SPROC, connection) {CommandType = CommandType.StoredProcedure, Transaction = transaction})
{
var reader = command.ExecuteReader();
if (reader.HasRows)
{
reader.Read();
ID = (Guid) reader["ID"];
Name = reader["Name"].ToString();
return this;
}
return null;
}
}
获取路径代码
public string GetFilePathUri(SqlConnection connection, SqlTransaction transaction)
{
using (var command = new SqlCommand(FILEPATH_SPROC, connection) {CommandType = CommandType.StoredProcedure, Transaction = transaction})
{
var reader = command.ExecuteReader();
if (reader.HasRows)
{
reader.Read();
return reader["Path"].ToString();
}
return "";
}
}
保存代码
public void SaveMessage(SqlConnection connection, SqlTransaction transaction)
{
using (var command = new SqlCommand(SAVE_SPROC, connection) {CommandType = CommandType.StoredProcedure, Transaction = transaction})
{
command.Parameters.Add("@ID", SqlDbType.UniqueIdentifier).Value = ID;
command.Parameters.Add("@Name", SqlDbType.VarChar).Value = Name;
//other object params here
command.ExecuteNonQuery();
}
}
问题
调用 transaction.Commit() 时,出现以下错误:
The transaction operation cannot be performed because there are pending requests working on this transaction.
我做错了什么?
编辑:快速编辑说我已经在 SO 上阅读了关于这个问题的其他问题,但找不到任何与 ADO.net
相关的问题
我以前遇到过这个问题,问题是 reader 需要关闭。
试试这个:
public foo Dequeue(SqlConnection connection, SqlTransaction transaction)
{
using (var command = new SqlCommand(DEQUEUE_SPROC, connection) {CommandType = CommandType.StoredProcedure, Transaction = transaction})
{
var reader = command.ExecuteReader();
if (reader.HasRows)
{
reader.Read();
ID = (Guid) reader["ID"];
Name = reader["Name"].ToString();
reader.Close();//Closing the reader
return this;
}
return null;
}
}
public string GetFilePathUri(SqlConnection connection, SqlTransaction transaction)
{
string filePathUri = "";
using (var command = new SqlCommand(FILEPATH_SPROC, connection) {CommandType = CommandType.StoredProcedure, Transaction = transaction})
{
var reader = command.ExecuteReader();
if (reader.HasRows)
{
reader.Read();
filePathUri = reader["Path"].ToString();
}
reader.Close();//Closing the reader
}
return filePathUri;
}
当我忘记在执行数据库调用的异步方法上使用 await 时,我遇到了这个问题 - 在交易 运行 时正在处理连接,因为程序没有等待查询在尝试处理所有内容之前完成。
我们 运行 解决了这个问题,虽然接受的答案可能有效,但这并不是最正确的做法——但是,它确实给了我们一个线索我们做错了什么。
在我们的例子中,有更多数据需要读取。这是问题的简化版本(使用 Dapper):
public Foo GetFooById(int fooId, IDbTransaction transaction = null) {
var sql = @"
SELECT * FROM dbo.Foo WHERE FooID = @FooID;
SELECT * FROM dbo.FooBar WHERE FooID = @FooID;
SELECT * FROM dbo.FooBaz WHERE FooID = @FooID;
";
using var data = await _connection.QueryMultipleAsync(sql, new { fooId }, transaction);
var foo = data.ReadSingle<Foo>();
foo.Bars = data.Read<Bar>();
// Oops, didn't read FooBaz.
return foo;
}
即使 data
被处置,它也会在 t运行 操作上留下一个“待处理的请求”,然后在调用 transaction.Commit()
时爆炸。
解决方案是从 data
中读取 FooBaz
条记录,或者删除未读的 SELECT
.
对于以后谁来这里,我遇到了 IQueryable
和 AutoMapper
组合的问题。
我传递了一个 IQueryable
用于映射到 Mapper.Map<>(myData)
,因为我认为既然它将数据从 class 映射到另一个,它也会实现它。
显然,我错了,在尝试 transaction.Commit()
时抛出了问题中提到的问题。
解决方案
就像在 Mapper.Map<>(myData.ToList())
中使用 .ToList()
强制实现一样简单。
只是浪费了一个小时才发现,我希望这会为其他人节省一些时间。
背景
我有一些代码可以打开 sql 连接,开始事务并对数据库执行一些操作。此代码从数据库创建一个对象(出队),获取一些值并将其保存回来。整个操作需要在事务中进行。所有代码在没有事务的情况下都能完美运行。
using (var connection = new SqlConnection(connectionString))
{
connection.Open();
var transaction = connection.BeginTransaction();
try
{
var myObject = foo.Dequeue(connection, transaction);
var url = myObj.GetFilePathUri(connection, transaction);
//some other code that sets object values
myObj.SaveMessage(connection, transaction);
transaction.Commit(); //error here
}
catch(Exception ex)
{
transaction.Rollback();
//logging
}
finally
{
//cleanup code
}
}
dequeue方法代码
public foo Dequeue(SqlConnection connection, SqlTransaction transaction)
{
using (var command = new SqlCommand(DEQUEUE_SPROC, connection) {CommandType = CommandType.StoredProcedure, Transaction = transaction})
{
var reader = command.ExecuteReader();
if (reader.HasRows)
{
reader.Read();
ID = (Guid) reader["ID"];
Name = reader["Name"].ToString();
return this;
}
return null;
}
}
获取路径代码
public string GetFilePathUri(SqlConnection connection, SqlTransaction transaction)
{
using (var command = new SqlCommand(FILEPATH_SPROC, connection) {CommandType = CommandType.StoredProcedure, Transaction = transaction})
{
var reader = command.ExecuteReader();
if (reader.HasRows)
{
reader.Read();
return reader["Path"].ToString();
}
return "";
}
}
保存代码
public void SaveMessage(SqlConnection connection, SqlTransaction transaction)
{
using (var command = new SqlCommand(SAVE_SPROC, connection) {CommandType = CommandType.StoredProcedure, Transaction = transaction})
{
command.Parameters.Add("@ID", SqlDbType.UniqueIdentifier).Value = ID;
command.Parameters.Add("@Name", SqlDbType.VarChar).Value = Name;
//other object params here
command.ExecuteNonQuery();
}
}
问题
调用 transaction.Commit() 时,出现以下错误:
The transaction operation cannot be performed because there are pending requests working on this transaction.
我做错了什么?
编辑:快速编辑说我已经在 SO 上阅读了关于这个问题的其他问题,但找不到任何与 ADO.net
相关的问题我以前遇到过这个问题,问题是 reader 需要关闭。 试试这个:
public foo Dequeue(SqlConnection connection, SqlTransaction transaction)
{
using (var command = new SqlCommand(DEQUEUE_SPROC, connection) {CommandType = CommandType.StoredProcedure, Transaction = transaction})
{
var reader = command.ExecuteReader();
if (reader.HasRows)
{
reader.Read();
ID = (Guid) reader["ID"];
Name = reader["Name"].ToString();
reader.Close();//Closing the reader
return this;
}
return null;
}
}
public string GetFilePathUri(SqlConnection connection, SqlTransaction transaction)
{
string filePathUri = "";
using (var command = new SqlCommand(FILEPATH_SPROC, connection) {CommandType = CommandType.StoredProcedure, Transaction = transaction})
{
var reader = command.ExecuteReader();
if (reader.HasRows)
{
reader.Read();
filePathUri = reader["Path"].ToString();
}
reader.Close();//Closing the reader
}
return filePathUri;
}
当我忘记在执行数据库调用的异步方法上使用 await 时,我遇到了这个问题 - 在交易 运行 时正在处理连接,因为程序没有等待查询在尝试处理所有内容之前完成。
我们 运行 解决了这个问题,虽然接受的答案可能有效,但这并不是最正确的做法——但是,它确实给了我们一个线索我们做错了什么。
在我们的例子中,有更多数据需要读取。这是问题的简化版本(使用 Dapper):
public Foo GetFooById(int fooId, IDbTransaction transaction = null) {
var sql = @"
SELECT * FROM dbo.Foo WHERE FooID = @FooID;
SELECT * FROM dbo.FooBar WHERE FooID = @FooID;
SELECT * FROM dbo.FooBaz WHERE FooID = @FooID;
";
using var data = await _connection.QueryMultipleAsync(sql, new { fooId }, transaction);
var foo = data.ReadSingle<Foo>();
foo.Bars = data.Read<Bar>();
// Oops, didn't read FooBaz.
return foo;
}
即使 data
被处置,它也会在 t运行 操作上留下一个“待处理的请求”,然后在调用 transaction.Commit()
时爆炸。
解决方案是从 data
中读取 FooBaz
条记录,或者删除未读的 SELECT
.
对于以后谁来这里,我遇到了 IQueryable
和 AutoMapper
组合的问题。
我传递了一个 IQueryable
用于映射到 Mapper.Map<>(myData)
,因为我认为既然它将数据从 class 映射到另一个,它也会实现它。
显然,我错了,在尝试 transaction.Commit()
时抛出了问题中提到的问题。
解决方案
就像在 Mapper.Map<>(myData.ToList())
中使用 .ToList()
强制实现一样简单。
只是浪费了一个小时才发现,我希望这会为其他人节省一些时间。