C# для профессионалов. Том II
Шрифт:
На практике большинство разработчиков используют только одну или две из этих настроек. Значение
Supported
подходит для классов типа класса Settings
, которому нужно будет обслуживать классы с транзакциями и без транзакций. Для большинства других транзакционных классов можно справиться, задавая значение Required
. Однако все-таки может возникнуть ситуация, где потребуются одно или несколько составных значений, дополнительную информацию можно найти в книге "Professinal Windows DNA Programming" (ISBN 1-861004-45-1) издательства Wrox Press. Кодирование транзакций с помощью ContextUtil
Модификация класса с помощью атрибута
Transaction
является только частью
ContextUtil
из пространства имен System.EnterpriseServices
. Проще говоря, класс
ContextUtil
предоставляет контекст транзакции. Когда имеется ссылка на контекст транзакции, можно явно заставить этот контекст зафиксировать или отменить транзакцию. Методы, которые необходимо вызвать для фиксации или отмены транзакций, представляются как методы класса в классе ContextUtil
, поэтому не придется создавать экземпляр класса ContextUtil
, чтобы их вызвать. В качестве примера сделаем краткий обзор фрагмента кода, приведенного ниже.
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);
}
// Если все прошло хорошо, завершить транзакцию.
ContextUtil.SetComplete;
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;
}
Поделиться с друзьями: