.Net 为 SqlClient 使用带有准备好的语句的事务
.Net Using Transactions with Prepared Statements for SqlClient
我正在尝试为脚本实施交易,但我 运行 遇到了一个奇怪的问题。
当我尝试 运行 事务内的准备好的 SQL 语句时,它失败了,因为它说在分配连接时它需要一个事务。
这如何与准备好的语句一起工作,因为我打算让多个事务都使用相同的准备好的语句。
我的代码如下
class dbTest {
public static SqlConnection db;
public static SqlCommand query;
static void Main(string[] args) {
db = connect();
prepare();
transaction01();
transaction02();
transaction03();
}
public static void prepare() {
query = new SqlCommand("select id from table where id = 1 for update", db);
query.Prepare();
}
public static void transaction01() {
SqlTransaction trans = db.BeginTransaction("Trn01");
SqlDataReader result = query.ExecuteReader();
while(result.Read()) { Console.WriteLine(result["id"]); }
result.Close();
trans.Commit();
}
public static void transaction02() {
SqlTransaction trans = db.BeginTransaction("Trn02");
SqlDataReader result = query.ExecuteReader();
while(result.Read()) { Console.WriteLine(result["id"]); }
result.Close();
trans.Commit();
}
public static void transaction03() {
SqlTransaction trans = db.BeginTransaction("Trn03");
SqlDataReader result = query.ExecuteReader();
while(result.Read()) { Console.WriteLine(result["id"]); }
result.Close();
trans.Commit();
}
}
如何将事务分配给现有的准备好的语句?
更新
更改了上面的代码以更好地显示问题。 SQL 只准备了一次,但我会将它用于多个交易(或者至少我想要)
再次更新
我已将下面的答案标记为正确答案,因为它看起来是实现此目标的最佳方法,但为了满足我的需要,在这个非常小的示例中使用 query.Transaction
得到了它的工作
public static void transaction01() {
SqlTransaction trans = db.BeginTransaction("Trn01");
query.Transaction = trans; // this line fixed it
SqlDataReader result = query.ExecuteReader();
while(result.Read()) { Console.WriteLine(result["id"]); }
result.Close();
trans.Commit();
}
使用 SqlTransaction 时,您必须明确设置 SqlCommand.Transaction,即使在 SQL 服务器中加入当前事务不是可选的。
select ... for update
无效 SQL 服务器语法,而是使用 UPDLOCK 读取 table 并在事务期间保留限制性锁。乙
select id from table with (updlock) where id = 1
When I attempt to run a Prepared SQL Statement
- 在 SQL 服务器上使用准备好的语句很少有用。即使没有它,查询计划缓存也会自动发生,当您使用不同的参数多次执行 SqlCommand 时,它实际上只是减少了网络上请求的大小。
但是准备好的 SqlCommand 仍然绑定到单个 SqlConnection,它的生命周期通常很短,从而最大限度地减少了准备 SqlCommand 的潜在好处。
您需要将SqlCommand.Transaction
设置为您的交易对象。
SQL 服务器没有必要准备语句。继续执行。
另请注意,如您在 this post 中所见,您 必须 正确处置所有数据库对象。
这是您清理后的代码:
class dbTest {
// DO NOT cache connection object
static void Main(string[] args) {
using(var db = connect())
using(var comm = GetCommand(db))
{
transaction01(comm);
transaction02(comm);
transaction03(comm);
}
}
public static SqlCommand GetCommand(SqlConnection conn) {
return new SqlCommand("select id from table with (updlock) where id = 1", conn);
}
public static void transaction01(SqlCommand comm) {
using(SqlTransaction trans = comm.Connection.BeginTransaction("Trn01"))
{
comm.Transaction = trans;
using(SqlDataReader result = query.ExecuteReader())
while(result.Read()) { Console.WriteLine(result["id"]); }
trans.Commit();
} // no need to close, using will sort that out
}
public static void transaction02(SqlCommand comm) {
using(SqlTransaction trans = comm.Connection.BeginTransaction("Trn02"))
{
comm.Transaction = trans;
using(SqlDataReader result = query.ExecuteReader())
while(result.Read()) { Console.WriteLine(result["id"]); }
trans.Commit();
} // no need to close, using will sort that out
}
public static void transaction03(SqlCommand comm) {
using(SqlTransaction trans = comm.Connection.BeginTransaction("Trn03"))
{
comm.Transaction = trans;
using(SqlDataReader result = query.ExecuteReader())
while(result.Read()) { Console.WriteLine(result["id"]); }
trans.Commit();
} // no need to close, using will sort that out
}
}
我正在尝试为脚本实施交易,但我 运行 遇到了一个奇怪的问题。
当我尝试 运行 事务内的准备好的 SQL 语句时,它失败了,因为它说在分配连接时它需要一个事务。
这如何与准备好的语句一起工作,因为我打算让多个事务都使用相同的准备好的语句。
我的代码如下
class dbTest {
public static SqlConnection db;
public static SqlCommand query;
static void Main(string[] args) {
db = connect();
prepare();
transaction01();
transaction02();
transaction03();
}
public static void prepare() {
query = new SqlCommand("select id from table where id = 1 for update", db);
query.Prepare();
}
public static void transaction01() {
SqlTransaction trans = db.BeginTransaction("Trn01");
SqlDataReader result = query.ExecuteReader();
while(result.Read()) { Console.WriteLine(result["id"]); }
result.Close();
trans.Commit();
}
public static void transaction02() {
SqlTransaction trans = db.BeginTransaction("Trn02");
SqlDataReader result = query.ExecuteReader();
while(result.Read()) { Console.WriteLine(result["id"]); }
result.Close();
trans.Commit();
}
public static void transaction03() {
SqlTransaction trans = db.BeginTransaction("Trn03");
SqlDataReader result = query.ExecuteReader();
while(result.Read()) { Console.WriteLine(result["id"]); }
result.Close();
trans.Commit();
}
}
如何将事务分配给现有的准备好的语句?
更新
更改了上面的代码以更好地显示问题。 SQL 只准备了一次,但我会将它用于多个交易(或者至少我想要)
再次更新
我已将下面的答案标记为正确答案,因为它看起来是实现此目标的最佳方法,但为了满足我的需要,在这个非常小的示例中使用 query.Transaction
得到了它的工作
public static void transaction01() {
SqlTransaction trans = db.BeginTransaction("Trn01");
query.Transaction = trans; // this line fixed it
SqlDataReader result = query.ExecuteReader();
while(result.Read()) { Console.WriteLine(result["id"]); }
result.Close();
trans.Commit();
}
使用 SqlTransaction 时,您必须明确设置 SqlCommand.Transaction,即使在 SQL 服务器中加入当前事务不是可选的。
select ... for update
无效 SQL 服务器语法,而是使用 UPDLOCK 读取 table 并在事务期间保留限制性锁。乙select id from table with (updlock) where id = 1
When I attempt to run a Prepared SQL Statement
- 在 SQL 服务器上使用准备好的语句很少有用。即使没有它,查询计划缓存也会自动发生,当您使用不同的参数多次执行 SqlCommand 时,它实际上只是减少了网络上请求的大小。
但是准备好的 SqlCommand 仍然绑定到单个 SqlConnection,它的生命周期通常很短,从而最大限度地减少了准备 SqlCommand 的潜在好处。
您需要将SqlCommand.Transaction
设置为您的交易对象。
SQL 服务器没有必要准备语句。继续执行。
另请注意,如您在 this post 中所见,您 必须 正确处置所有数据库对象。
这是您清理后的代码:
class dbTest {
// DO NOT cache connection object
static void Main(string[] args) {
using(var db = connect())
using(var comm = GetCommand(db))
{
transaction01(comm);
transaction02(comm);
transaction03(comm);
}
}
public static SqlCommand GetCommand(SqlConnection conn) {
return new SqlCommand("select id from table with (updlock) where id = 1", conn);
}
public static void transaction01(SqlCommand comm) {
using(SqlTransaction trans = comm.Connection.BeginTransaction("Trn01"))
{
comm.Transaction = trans;
using(SqlDataReader result = query.ExecuteReader())
while(result.Read()) { Console.WriteLine(result["id"]); }
trans.Commit();
} // no need to close, using will sort that out
}
public static void transaction02(SqlCommand comm) {
using(SqlTransaction trans = comm.Connection.BeginTransaction("Trn02"))
{
comm.Transaction = trans;
using(SqlDataReader result = query.ExecuteReader())
while(result.Read()) { Console.WriteLine(result["id"]); }
trans.Commit();
} // no need to close, using will sort that out
}
public static void transaction03(SqlCommand comm) {
using(SqlTransaction trans = comm.Connection.BeginTransaction("Trn03"))
{
comm.Transaction = trans;
using(SqlDataReader result = query.ExecuteReader())
while(result.Read()) { Console.WriteLine(result["id"]); }
trans.Commit();
} // no need to close, using will sort that out
}
}