Ukn*_*ght 5 c# sqlcommand sqlconnection sqltransaction
我试图只使用一个连接并一起运行两个命令,一个使用事务,一个不使用.
没有跟踪/日志记录功能,因为此解决方案部署在另一个位置.因此,当流程部分失败时,我至少可以按照日志进行操作.
我将在这里添加我的测试代码:
SqlConnection connection = GetConnection();
SqlTransaction transaction = null;
try
{
connection.Open();
transaction = connection.BeginTransaction();
SqlCommand logCommand = new SqlCommand("Log before main command", connection);
logCommand.ExecuteNonQuery();
string sql = "SELECT 1";
SqlCommand command = new SqlCommand(sql, connection, transaction);
int rows = command.ExecuteNonQuery();
logCommand = new SqlCommand("Log after main command", connection);
logCommand.ExecuteNonQuery();
// Other similar code
transaction.Commit();
command.Dispose();
}
catch { /* Rollback etc */ }
finally { /* etc */ }
Run Code Online (Sandbox Code Playgroud)
我收到一个错误:
当分配给命令的连接处于挂起的本地事务中时,ExecuteNonQuery要求命令具有事务.该命令的Transaction属性尚未初始化.
如果没有其他无交易连接,有没有办法实现我想要做的事情?
或者,如果有一个更好的建议,以不同的方式通过单个连接优化我的代码,我就可以开始学习它.
错误发生在这里:
SqlConnection connection = GetConnection();
SqlTransaction transaction = null;
try
{
connection.Open();
transaction = connection.BeginTransaction();
SqlCommand logCommand = new SqlCommand("Log before main command", connection); // <--- did not give the transaction to the command
logCommand.ExecuteNonQuery(); // <--- Exception: ExecuteNonQuery requires the command to have a transaction ...
string sql = "SELECT 1";
SqlCommand command = new SqlCommand(sql, connection, transaction);
int rows = command.ExecuteNonQuery();
logCommand = new SqlCommand("Log after main command", connection);
logCommand.ExecuteNonQuery(); // <--- Same error also would have happened here
// Other similar code
transaction.Commit();
command.Dispose();
}
catch { /* Rollback etc */ }
finally { /* etc */ }
Run Code Online (Sandbox Code Playgroud)
发生这种情况的原因是,当连接在事务中登记时,该连接上的所有命令都在该事务中。换句话说,您不能让命令“选择退出”事务,因为事务适用于整个连接。
不幸的是,SqlClient API 有点误导,因为在调用 connection.BeginTransaction() 之后,您仍然必须将 SqlTransaction 提供给 SqlCommand。如果您没有显式地将事务交给命令,那么当您执行该命令时,SqlClient 会为此责备您(“不要忘记告诉我有关我已经知道我所在的事务的信息!”)这是你看到的例外。
这种笨拙是一些人更喜欢使用 TransactionScope 的原因之一,尽管我个人不喜欢将 TransactionScope 用于非分布式事务,因为它的“隐式魔法”API 以及与异步的不良交互。
如果您不希望“log”命令与主命令位于同一事务中,则必须为它们使用另一个连接,或者仅在该主命令的持续时间内保留事务:
try
{
connection.Open();
// No transaction
SqlCommand logCommand = new SqlCommand("select 'Log before main command'", connection);
logCommand.ExecuteNonQuery();
// Now create the transaction
transaction = connection.BeginTransaction();
string sql = "SELECT 1";
SqlCommand command = new SqlCommand(sql, connection, transaction);
int rows = command.ExecuteNonQuery();
transaction.Commit();
// Transaction is completed, now there is no transaction again
logCommand = new SqlCommand("select 'Log after main command'", connection);
logCommand.ExecuteNonQuery();
// Other similar code
command.Dispose();
}
//catch { /* Rollback etc */ }
finally
{
if (transaction != null)
{
transaction.Dispose();
}
}
Run Code Online (Sandbox Code Playgroud)
如果您确实希望他们成为交易的一部分,则必须明确地将交易交给他们:
SqlConnection connection = GetConnection();
SqlTransaction transaction = null;
try
{
connection.Open();
transaction = connection.BeginTransaction();
SqlCommand logCommand = new SqlCommand("select 'Log before main command'", connection, /* here */ transaction);
logCommand.ExecuteNonQuery();
string sql = "SELECT 1";
SqlCommand command = new SqlCommand(sql, connection, transaction);
int rows = command.ExecuteNonQuery();
logCommand = new SqlCommand("select 'Log after main command'", connection, /* and here */ transaction);
logCommand.ExecuteNonQuery();
// Other similar code
transaction.Commit();
command.Dispose();
}
//catch { /* Rollback etc */ }
finally
{
if (transaction != null)
{
transaction.Dispose();
}
}
Run Code Online (Sandbox Code Playgroud)