在.NET中将ADO.Net驱动程序用于MySQL时,是否有必要将事务分配给MySqlCommand对象(例如,使用oCmd.Transaction = oTran),或者MySqlConnection对象具有开放式事务是否足够?在下面的代码中,我开始在连接上进行事务,使用单独的MySqlCommand对象运行两个查询而不分配事务,然后回滚事务。在此示例中,UPDATE均未提交到数据库。



MySqlConnection oConn = new MySqlConnection("Server=spet-il-cent-02;Port=3306;Database=test;Uid=test;Pwd=test123;CharSet=utf8;");
oConn.Open();
MySqlTransaction oTran = oConn.BeginTransaction();
MySqlCommand oCmd = oConn.CreateCommand();

oCmd.CommandText = "UPDATE testing SET testBalance = testBalance + 10 WHERE testID = 1";
oCmd.ExecuteNonQuery();

oCmd = oConn.CreateCommand();
oCmd.CommandText = "UPDATE testing SET testBalance = testBalance - 10 WHERE testID = 2";
oCmd.ExecuteNonQuery();

oTran.Rollback();

oConn.Close();


在运行时检查TransactionoCmd属性时,我看到它是null

显然,如果我调用oTran.Commit(),则两个UPDATE语句都将提交。那么Transaction对象的MySqlCommand属性的目的是什么?是否在单个连接上允许多个并发事务(其中,不同的命令将绑定到不同的事务,并且可以相互回滚或提交,而不考虑彼此)?

最佳答案

对于单个语句,您可以将属性与Commit()Rollback()方法一起假装不存在。如果该属性不存在,则自动提交单个语句。在问题代码中,有可能在testID = 1更新和testID = 2更新以及Rollback()方法无法完成任何操作之间的短暂间隔内运行查询。

要利用MySqlTransaction对象,您需要执行以下操作:

using (var Conn = new MySqlConnection("Server=spet-il-cent-02;Port=3306;Database=test;Uid=test;Pwd=test123;CharSet=utf8;"))
{
    Conn.Open();
    MySqlTransation Tran = Conn.BeginTransaction();

    using (var Cmd = new MySqlCommand("UPDATE testing SET testBalance = testBalance + 10 WHERE testID = 1", Conn))
    {
        Cmd.Transaction = Tran;
        Cmd.ExecuteNonQuery();
    }
    using (var Cmd = new MySqlCommand("UPDATE testing SET testBalance = testBalance + 10 WHERE testID = 2", Conn))
    {
        Cmd.Transaction = Tran;
        Cmd.ExecuteNonQuery();
    }
    Tran.Rollback();
}


甚至更好:

using (var Conn = new MySqlConnection("Server=spet-il-cent-02;Port=3306;Database=test;Uid=test;Pwd=test123;CharSet=utf8;"))
using (var Cmd = new MySqlCommand("UPDATE testing SET testBalance = testBalance + 10 WHERE testID = @testID", Conn))
{
    Conn.Open();
    Cmd.Transaction = Conn.BeginTransaction();

    Cmd.Parameteres.Add("@testID", MySqlDbType.Int32).Value = 1;
    Cmd.ExecuteNonQuery();
    Cmd.Parameters["testID"].Value = 2; //I can't remember at the moment if you need the "@" here or not
    Cmd.ExecuteNonQuery();

    Cmd.Transaction.Rollback();
}


主要是在您要保证多个操作是原子操作时才需要事务。当您分别将事务分配给命令对象的每个实例,命令的每个实例使用相同的连接并且在事务期间连接保持打开状态时,此方法有效。

此外,您可以将多个语句放入单个命令对象中:

string sql =
  "BEGIN;" +
  "UPDATE testing SET testBalance = testBalance + 10 WHERE testID = 1;" +
  "UPDATE testing SET testBalance = testBalance - 10 WHERE testID = 2;" +
  "COMMIT;";

using (var Conn = new MySqlConnection("Server=spet-il-cent-02;Port=3306;Database=test;Uid=test;Pwd=test123;CharSet=utf8;"))
using (var Cmd = new MySqlCommand(sql, Conn))
{
    Conn.Open();
    Cmd.ExecuteNonQuery();
}


这是首选的方法,但是有时代码的性质会阻止它,因此您必须使用MySqlTransaction对象。

10-06 07:06