Я пытаюсь реализовать транзакции для скрипта, но столкнулся со странной проблемой.

Когда я пытаюсь запустить подготовленный оператор 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();
    }
1
TheLovelySausage 18 Янв 2021 в 19:33

2 ответа

Лучший ответ

Для объекта транзакции необходимо указать SqlCommand.Transaction.

В SQL Server нет необходимости подготавливать оператор. Просто продолжай выполнять.

Также обратите внимание, как вы можете видеть в этом сообщении, что вы должны < / strong> правильно утилизировать все объекты БД.

Вот ваш код очищен:

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
    }

}
1
Charlieface 18 Янв 2021 в 21:51
  1. При работе с SqlTransaction необходимо установить SqlCommand.Transaction явно, даже несмотря на то, что присоединение к текущей транзакции не является необязательным в SQL Server.

  2. select ... for update не является допустимым синтаксисом SQL Server, вместо этого используйте UPDLOCK для чтения таблицы и сохранения ограничительной блокировки на время транзакции. НАПРИМЕР

    select id from table with (updlock) where id = 1

Когда я пытаюсь запустить подготовленный оператор SQL

  1. Редко бывает полезно использовать подготовленные операторы с SQL Server. Кэширование плана запроса происходит автоматически даже без него, и на самом деле оно просто уменьшает размер запроса в сети, когда вы выполняете SqlCommand много раз с разными параметрами.

Но подготовленный SqlCommand по-прежнему привязан к одному SqlConnection, который обычно имеет короткое время жизни, что сводит к минимуму потенциальные преимущества подготовки SqlCommand.

2
David Browne - Microsoft 18 Янв 2021 в 17:01