throw new Exception("Sorry! Database error! Tx failed...");
// Возникла ошибка, связанная с базой данных! Отказ транзакции...
}
// Зафиксировать транзакцию!
tx.Commit;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
// Любая ошибка приведет к откату транзакции.
// Использовать условную операцию для проверки на предмет null.
tx?.Rollback;
}
finally
{
CloseConnection;
}
}
Здесь используется входной параметр типа
bool
, который указывает, нужно ли генерировать произвольное исключение при попытке обработки проблемного клиента. Такой прием позволяет эмулировать непредвиденные обстоятельства, которые могут привести к неудачному завершению транзакции. Понятно, что это делается лишь в демонстрационных целях; настоящий метод транзакции не должен позволять вызывающему процессу нарушать работу логики по своему усмотрению!
Обратите внимание на применение двух объектов
SqlCommand
для представления каждого шага транзакции, которая будет запущена. После получения имени и фамилии клиента на основе входного параметра
customerID
с помощью метода
BeginTransaction
объекта подключения можно получить допустимый объект
SqlTransaction
. Затем (что очень важно) потребуется привлечь к участию каждый объект команды, присвоив его свойству
Transaction
полученного объекта транзакции. Если этого не сделать, то логика вставки и обновления не будет находиться в транзакционном контексте.
После вызова метода
ExecuteNonQuery
на каждой команде генерируется исключение, если (и только если) значение параметра
bool
равно
true
. В таком случае происходит откат всех ожидающих операций базы данных. Если исключение не было сгенерировано, тогда в результате вызова
Commit
оба шага будут зафиксированы в таблицах базы данных.
Тестирование транзакции базы данных
Выберите одного из клиентов, добавленных в таблицу
Customers
(например,
Dave Benner
,
Id = 1
).
Добавьте в
Program.cs
внутри проекта
AutoLot.Client
новый метод по имени
FlagCustomer
:
void FlagCustomer
{
Console.WriteLine("***** Simple Transaction Example *****\n");
// Простой способ позволить транзакции успешно завершиться или отказать.
bool throwEx = true;
Console.Write("Do you want to throw an exception (Y or N): ");
Console.WriteLine("Check CreditRisk table for results");
// Результаты ищите в таблице CreditRisk
Console.ReadLine;
}
Если вы запустите программу и укажете на необходимость генерации исключения, то обнаружите, что фамилия клиента в таблице
Customers
не изменилась, т.к. произошел откат всей транзакции. Однако если исключение не генерировалось, тогда окажется, что фамилия клиента в таблице
Customers
изменилась и была добавлена в таблицу
CreditRisks
.
Выполнение массового копирования с помощью ADO.NET
В случае, когда необходимо загрузить много записей в базу данных, показанные до сих пор методы будут довольно неэффективными. В SQL Server имеется средство, называемое массовым копированием, которое предназначено специально для таких сценариев, и в ADO.NET для него предусмотрена оболочка в виде класса
SqlBulkCopy
. В настоящем разделе главы объясняется, как выполнять массовое копирование с помощью ADO.NET.
Исследование класса SqlBulkCopy
Класс
SqlBulkCopy
имеет один метод,
WriteToServer
(и его асинхронную версию
WriteToServerAsync
), который обрабатывает список записей и помещает данные в базу более эффективно, чем последовательность операторов
Insert
, выполненная с помощью объектов команд. Метод
WriteToServer
перегружен, чтобы принимать объект
DataTable
, объект
DataReader
или массив объектов
DataRow
. Придерживаясь тематики главы, мы собираемся использовать версию
WriteToServer
, которая принимает
DataReader
, так что необходимо создать специальный класс чтения данных.