Откаты сложны - AFAIK, на самом деле есть только 2 способа сделать это. Либо 2-фазный протокол фиксации , либо компенсирующие транзакции . Вы действительно должны найти способ структурировать свои задачи в одном из этих способов.
Как правило, лучшей идеей является использование преимуществ тяжелой работы других людей и использование технологий, в которых уже есть 2PC или встроенная компенсация. Это одна из причин, по которой СУБД так популярны.
Итак, специфика зависит от задачи ... но шаблон довольно прост:
class Compensator {
Action Action { get; set; }
Action Compensate { get; set; }
}
Queue<Compensator> actions = new Queue<Compensator>(new Compensator[] {
new Compensator(SendEmail, UndoSendEmail),
new Compensator(ArchiveReportsInDatabase, UndoArchiveReportsInDatabase),
new Compensator(CreateAFile, UndoCreateAFile)
});
Queue<Compensator> doneActions = new Queue<Compensator>();
while (var c = actions.Dequeue() != null) {
try {
c.Action();
doneActions.Add(c);
} catch {
try {
doneActions.Each(d => d.Compensate());
} catch (EXception ex) {
throw new OhCrapException("Couldn't rollback", doneActions, ex);
}
throw;
}
}
Конечно, для ваших конкретных задач - вам может повезти.
- Очевидно, что работа СУБД уже может быть включена в транзакцию.
- Если вы работаете в Vista или Server 2008, вы получаете Транзакционная NTFS для покрытия вашего сценария CreateFile.
- Электронная почта немного сложнее - я не знаю каких-либо 2PC или компенсаторов вокруг нее (хотя я был бы немного удивлен, если бы кто-то указал, что у Exchange есть такой), поэтому я бы, вероятно, использовал MSMQ написать уведомление и позволить абоненту забрать его и в конечном итоге отправить его по электронной почте. В этот момент ваша транзакция действительно охватывает только отправку сообщения в очередь, но, вероятно, этого достаточно.
Все они могут участвовать в транзакции System.Transactions , поэтому вы должны быть в хорошей форме.