I have a method which internally performs different sub-operations in an order and at failure of any of the sub operation i want to Rollback the entire operation.
My issue is the sub-operations are not all database operations. These are mainly system level changes like adding something in windows registry, creating a folder at a specified path and setting permissions etc. the sub-operations can be more than this.
want to do somthing like this;
CreateUser(){
CreateUserFtpAccount();
CreateUserFolder();
SetUserPermission();
CreateVirtualDirectoryForUser();
....
....
....
and many more
}
if last operation fails, i want to roll back all previous operations.
So, what is the standard way to do this? is there a design pattern do handle this?
Note: i'm using C#.net
Here's one way to do it:
Using the command pattern, you can create undoable actions. With each operation, you register the related commands, so that you can undo the executed commands when a fail condition occurs.
For example, this might all belong in a transaction-like context object that implements IDisposable
and put in a using
block. The undoable actions would be registered to this context object. On dispose, if not committed, "undo" is carried out for all registered commands. Hope it helps. The downside is you may have to convert some methods to classes. This might be a necessary evil though.
Code sample:
using(var txn = new MyTransaction()) {
txn.RegisterCommand(new CreateUserFtpAccountCommand());
txn.RegisterCommand(new CreateUserFolderCommand());
txn.RegisterCommand(new SetUserPermissionCommand());
txn.RegisterCommand(new CreateVirtualDirectoryForUserCommand());
txn.Commit();
}
class MyTransaction : IDisposable {
public void RegisterCommand(Command command){ /**/ }
public void Commit(){ /* Runs all registered commands */ }
public void Dispose(){ /* Executes undo for all registered commands */ }
}
class UndoableCommand {
public Command(Action action) { /**/ }
public void Execute() { /**/ }
public void Undo{ /**/ }
}
Update:
You mentioned that you have hundreds of such reversible operations. In this case, you can take a more functional approach and get rid of UndoableCommand
completely. You would register delegates instead, like this:
using(var txn = new MyTransaction()) {
txn.Register(() => ftpManager.CreateUserAccount(user),
() => ftpManager.DeleteUserAccount(user));
txn.Register(() => ftpManager.CreateUserFolder(user, folder),
() => ftpManager.DeleteUserFolder(user, folder));
/* ... */
txn.Commit();
}
class MyTransaction : IDisposable {
public void Register(Action operation, Action undoOperation){ /**/ }
public void Commit(){ /* Runs all registered operations */ }
public void Dispose(){ /* Executes undo for all registered and attempted operations */ }
}
As a note, you'd need to be careful with closures with this approach.
I think your best bet would be to encapsulate the execution and reversal of each step of the process. It will make a lot more easily read code than nested try-catch blocks. Something like:
public interface IReversableStep
{
void DoWork();
void ReverseWork();
}
public void DoEverything()
{
var steps = new List<IReversableStep>()
{
new CreateUserFTPAccount(),
new CreateUserFolder(),
...
}
var completed = new List<IReversableStep>();
try
{
foreach (var step in steps)
{
step.DoWork();
completed.Add(step);
}
}
catch (Exception)
{
//if it is necessary to undo the most recent actions first,
//just reverse the list:
completed.Reverse();
completed.ForEach(x => x.ReverseWork());
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With