Ватсон Карли
Шрифт:
return true;
}
// Этот код выполняется, если встречается ошибка.
catch (Exception е) {
// Отменить работу, которую выполнила эта функция.
ContextUtil.SetAbort;
return false;
}
}
Что здесь происходит?
Мы имеем две транзакции, которые обрабатываются в зависимости от значения
CommitTrans
. Для любой транзакции PlaceOrder
вызывает два метода, которые оба соединяющиеся с базой данных Northwind
, чтобы сделать изменения в таблице Product
. Метод ReduceStock
сокращает объем запасов в столбце UnitsInStock
, метод IncreaseUnits
увеличивает значение столбца UnitsOnOrder
. Для обоих методов первым параметром является ProductID
в строке, которую нужно изменить, второй параметр есть величина, на которую мы хотим изменить соответствующий столбец. Выполняющаяся транзакция контролируется булевой переменной
CommitTrans
, передаваемой в PlaceOrder
. Первая транзакция должна быть зафиксирована, так как уровень запаса для ProductID=2
равен 17, следовательно, можно удалить десять элементов и все еще иметь оставшийся запас. Однако вторая транзакция обречена на отказ так как ProductID=5
не имеет запаса элементов и существует ограничение на столбец UnitsInStock
, которое не позволяет значению становиться меньше нуля. Это означает, что можно проверить, будет ли транзакция отменяться или нет. Не должно быть никаких проблем с вызовом IncreaseStock
, поэтому можно увидеть, что транзакция была отменена, проверяя значение столбца UnitsOnOrder
для ProductID=5
. В блоке
try
, если все идет хорошо, или, другими словами, если поток выполнения должен покинуть PlaceOrder
нормально, через return true
, инструкция PlaceOrder
вызывает метод SetComplete
объекта ContextUtil
, эффективно сообщая DTC через менеджер ресурсов, что в той части, которая касается его, транзакцию необходимо зафиксировать. С другой стороны, если где-то в
PlaceOrder
возникает ошибка и порождается исключение, управление программой будет передано предложению catch
. В этом предложении PlaceOrder
вызовет метод SetAbort
объекта ContextUtil
. Этот метод посылает голос PlaceOrder
за отмену транзакции, в которую он вовлечен, и DTC после получения этого голоса от менеджера ресурсов прикажет каждому участнику транзакции отменить свою работу. Помните, что не требуется создавать экземпляр объекта
ContextUtil
, чтобы вызвать его методы SetComplete
и SetAbort
. Эти методы класса, поэтому их можно вызывать прямо на классе. Практически любой код, поддерживающий транзакции будет походить на код в примере. Он вызывает
SetComplete
перед своей точкой выхода для фиксации всей работы, которая была успешно выполнена, или он вызывает метод SetAbort
класса ContextUtil
в своем обработчике ошибок, чтобы все отменить в связи с ошибкой. Существует, правда, еще более простой способ. Компания Microsoft предоставляет атрибут .NET, называемый
AutoComplete
. Методы, модифицированные с помощью этого атрибута, автоматически применяют подход, описанный выше. И хотя такие методы никогда явно не ссылаются на класс ContextUtil
, они неявно завершают свои транзакции, если те заканчиваются нормально, или отменяют всю работу если выход происходит в связи с ошибкой (когда порождается исключение). По прежнему необходимо вызывать SetAbort
, чтобы отменить работу транзакции если порождается исключение. [AutoComplete]
public bool PlaceOrder(bool CommitTrans) {
try {
if (CommitTrans) {
// Эта транзакция должна быть зафиксирована
// шаг 1 — Увеличить число единиц продукта ID=2 на 10
IncreaseUnits(2, 10);
// шаг 2 - Сократить запас продукта ID=2 на 10 единиц
ReduceStock(2, 10);
} else {
// Эта транзакция должна быть отменена
// шаг 1 - Увеличить число единиц продукта ID=5 на 5
IncreaseUnits(5, 5);
// шаг 2 — Сократить запас продукта ID=5 на 5 единиц
ReduceStock(5, 5);
}
return true;
} catch (Exception e) {
ContextUtil.SetAbort;
return false;
}
}
Это полный код примера всей транзакции, он показывает, как все части объединяются. Следующее просто встроено в библиотеку классов с заданным сильным именем и зарегистрировано в глобальном кэше сборок.
using System;
using System.EnterpriseServices;
using System.Data.SqlClient;
namespace OrderTransaction {
[Transaction(TransactionOptiоn.Required)]
public class Purchase : ServicedComponent {