debugcsharpMinor
Making an atomic transaction out of several operations
Viewed 0 times
operationsseveraltransactionmakingoutatomic
Problem
I'm looking for a design pattern or suggestions that can help refactor my code into something a bit less repetitive. I have a method that has several sequential steps (10-15) that if any of them fail must record detail about the failure to a log and rollback all previous portions of the transaction. The example is below.
I have thought about taking each try catch and turning it into an individual method but then I have to pass around my undo stack (among several other variables) and I still have to conditionally abort the method and process the undo stack. I've briefly looked into the Momento and Command patterns but both seemed to grow the line count by a fair margin.
I have thought about taking each try catch and turning it into an individual method but then I have to pass around my undo stack (among several other variables) and I still have to conditionally abort the method and process the undo stack. I've briefly looked into the Momento and Command patterns but both seemed to grow the line count by a fair margin.
EnumResult TransactionThatRollsBack()
{
var undoLog = new Stack();
try
{
MoveInventory(inv, src, dest);
undoLog.Push(() => MoveInventory(inv,dest,src));
}
catch (Exception ex)
{
RecordError("InventoryError", ex, inv, src, dest);
Undo(undoLog);
return EnumResult.FailedInventoryMove;
}
try
{
NotifyReportingOfMove(inv, user);
undoLog.Push(() => NotifyReportingOfUnMove(inv, user));
}
catch (Exception ex)
{
RecordError("NotifyReportingOfMove", ex, inv, user);
Undo(undoLog);
return EnumResult.FailedReportingMove;
}
try
{
AddUserToSweepstakes(user);
undoLog.Push(() => RemoevUserFromSweepstakes(user));
}
catch (Exception ex)
{
RecordError("AddUserToSweepstakes", ex, user);
Undo(undoLog);
return EnumResult.FailedSweepstakesAdd;
}
...
return EnumResult.Success
}Solution
You can remove repeated code if you record all your actions for executions and then execute them all at once with some generic exception handling. The idea is below.
Sorry for some possible lapses, didn't write in C# for quite some time. However I hope that you'll find the whole idea to be helpful.
Sorry for some possible lapses, didn't write in C# for quite some time. However I hope that you'll find the whole idea to be helpful.
EnumResult TransactionThatRollsBack()
{
var undoLog = new SmartStack();
undoLog.Push(() => MoveInventory(inv,dest,src), EnumResult.1);
undoLog.Push(() => NotifyReportingOfUnMove(inv, user), EnumResult.2);
undoLog.Push(() => RemoevUserFromSweepstakes(user), EnumResult.3);
undoLog.execute();
}
class SmartStack {
SortedDictionary actions;
void push(Action a, EnumResult r) {
actions.add(a, r);
}
void execute() {
Stack completedActions = new Stack();
foreach(KeyValuePair pair in actions) {
try {
pair.Key.Invoke();
completedActions.push(pair.Key);
}
catch(Exception ex) {
for (Action a in completedActions) {
Undo(a);
}
return pair.Value;
}
}
}
}Code Snippets
EnumResult TransactionThatRollsBack()
{
var undoLog = new SmartStack();
undoLog.Push(() => MoveInventory(inv,dest,src), EnumResult.1);
undoLog.Push(() => NotifyReportingOfUnMove(inv, user), EnumResult.2);
undoLog.Push(() => RemoevUserFromSweepstakes(user), EnumResult.3);
undoLog.execute();
}
class SmartStack {
SortedDictionary<Action, EnumResult> actions;
void push(Action a, EnumResult r) {
actions.add(a, r);
}
void execute() {
Stack<Action> completedActions = new Stack<Action>();
foreach(KeyValuePair<Action, EnumResult> pair in actions) {
try {
pair.Key.Invoke();
completedActions.push(pair.Key);
}
catch(Exception ex) {
for (Action a in completedActions) {
Undo(a);
}
return pair.Value;
}
}
}
}Context
StackExchange Code Review Q#42920, answer score: 8
Revisions (0)
No revisions yet.