Выполнение нескольких операторов / sqlcomands в одной транзакции ado.net

Я не включаю здесь какой-либо код, потому что я переписывал его так много раз за последние несколько дней, что его действительно не стоит беспокоить, поэтому я объясню свою проблему в общих чертах.

У меня есть приложение winforms, написанное на C# и подключенное к базе данных SQL Server. В некоторых частях моего приложения у меня может быть до 10 SQL-команд, работающих с многочисленными таблицами в базе данных, и которые мне нужно содержать в одной транзакции, потому что это ситуация "все или ничего". Если какая-либо из 10 команд терпит неудачу, я хочу, чтобы ни одна из команд не была зафиксирована.

Осложняющие факторы включают в себя:

  1. Каждая из 10 команд sql имеет несколько параметров
  2. Некоторые из команд sql используют INSERT, и мне нужно использовать сгенерированный сервером идентификатор в некоторых последующих командах sql.
  3. Некоторые из команд работают с одними и теми же данными, т. Е. Более ранняя команда может ВСТАВИТЬ новый элемент в базу данных, а одна из более поздних команд sql попытается обновить эту же запись.

Любая помощь была бы очень признательна, так как я нахожусь в своем уме, поскольку этот вопрос я провел большую часть недели и чувствую, что достигнут относительно небольшой прогресс.

РЕДАКТИРОВАТЬ:

Команды sql изначально были в разных классах и т. Д., Но я подумал, что это может быть проблемой, поэтому я сделал большую переписку, и теперь ВСЕ команды sql находятся в одном методе, хотя некоторые из команд sql записаны в другом месте и переданы обратно основной метод.

Проблема на данный момент заключается в том, что первая команда (INSERT) выполняется, но транзакция завершается неудачно со второй командой, потому что она пытается обратиться к записи базы данных, созданной в первой команде.

Также я не могу использовать TransactionScope, потому что MSDTC в сети вызывает другие проблемы, и поэтому мне нужно справиться с этим с моим приложением и ADO.NET.

РЕДАКТИРОВАТЬ (НЕКОТОРЫЙ КОД)

Вот только первые две команды sql в транзакции:

string connectionString = aSystem.ConnectionString;

    using (SqlConnection linkToDB = new SqlConnection(connectionString))
    {
        linkToDB.Open();
        using (SqlTransaction transaction = linkToDB.BeginTransaction())
        {
            try
            {                            
                //...Case...//

                cCase c = new cCase();
                c.CaseType = cboCaseType.Text;
                c.Occupation = txtOccupation.Text;
                c.DateInstructed = txtDateInst.Text;
                c.Status = "Live";
                SqlCommand sqlSaveCase = c.SaveSqlCom();                               
                sqlSaveCase.Connection = linkToDB;
                sqlSaveCase.Transaction = transaction;
                c.SetCaseNo = sqlSaveCase.ExecuteNonQuery().ToString();

                //...IP Link...//

                string strIPID = cboIPAddress.SelectedValue.ToString();
                SqlCommand sqlNewIPLink = cIPID.NewIPLinkSqlCom(strIPID,c.CaseNo,txtIPRef.Text);
                sqlNewIPLink.Connection = linkToDB;
                sqlNewIPLink.Transaction = transaction;
                sqlNewIPLink.ExecuteNonQuery();                //...fails here...//


                //...an additional 8 sql commands follow...//

ВОПРОС

Команда sqlSaveCase ВСТАВЛЯЕТ новый случай в tblCases, но когда вторая команда sqlNewIPLink пытается использовать идентификатор, созданный первым, она генерирует ошибку внешнего ключа, как будто она не видит вновь созданный случай из первой команды.

4 ответа

Решение

Ошибка была в этой строке

c.SetCaseNo = sqlSaveCase.ExecuteNonQuery().ToString(); 

который должен использовать ExecutreScalar() для получения идентификатора из команды INSERT. Подсказка появилась, когда я в конце концов понял, что всегда возвращал 1, что, я полагаю, является просто логическим значением "true", чтобы сказать, что команда была выполнена. Как только я использовал ExecuteScalar в команде, он начал возвращать более значимые идентификаторы, которые распознавались последующими SqlCommands, и, следовательно, никаких проблем с ключом Foregin.

Не уверен, что это проблема, но я не вижу, где вы используете c.SetCaseNo во второй SqlCommand.

  //
  // here you are setting c.SetCaseNo
  //
  c.SetCaseNo = sqlSaveCase.ExecuteNonQuery().ToString();

  string strIPID = cboIPAddress.SelectedValue.ToString();

  //
  // but here you're using c.CaseNo
  //
  SqlCommand sqlNewIPLink = cIPID.NewIPLinkSqlCom(strIPID,c.CaseNo,txtIPRef.Text);


  sqlNewIPLink.Connection = linkToDB;
  sqlNewIPLink.Transaction = transaction;
  sqlNewIPLink.ExecuteNonQuery();   

В вашем случае рекомендуется использовать шаблон команд. На самом деле, это позволяет вам отменить ваши операции. Транзакция хороша для нескольких операций. Но с 10 операциями лучше выполнять каждую команду в цикле и, если одна команда не удалась, отменить команду. Это подразумевает, что вы реализуете операцию отката (здесь 10). здесь ссылка с образцом шаблона:http://www.dofactory.com/Patterns/PatternCommand.aspx

Вы можете использовать класс TransactionScope для него.

Вот хорошее объяснение этого.

надеюсь это поможет.

Другие вопросы по тегам