Creating loosely coupled and generic process steps implementation

Published on Thursday, May 2, 2013

Recently, during one of the SharePoint implementations we came across the requirement to build the workflow for site provisioning engine that we are developing.

One of the requirements of the process is to rollback everything that we create/modify if there is any problem/exception during the process. So for example, if we created the site collection and then in the next step process failed at setting up custom permissions, the process should be roll backed to the initial stage.

Normally, this is no big deal to implement everything as a standard workflow and you write a piece of code for different activities of the workflow (or use standard workflow activities from Visual Studio toolbox) but then an idea came up why not write an implementation of the process steps independent of the workflow in a nice and loosely couple way that should take care of roll back in a much flexible and nicer and cleaner way !

So in this post, I will try to describe the solution that I came up with and also implementation details.

So here is the implementation details:

First, I created an interface that contains the method declarations for Execute and RollBack methods


public interface IProcessStep
{
   void Execute(ProcessStepProperties properties);

   void RollBack(ProcessStepProperties properties);
}

As you see in the code above, I'm using parameter of type ProcessStepProperties. This is basically a custom class that I use as a data transfer object (DTO) across different steps and methods. It's basically a collection (dictionary in my implementation) that contain key/value pairs. Here is the implementation:


public class ProcessStepProperties
{
   public Dictionary<String, Object> Keys = new Dictionary<String, Object>();

   public bool KeyExists(String key)
   {
     if (Keys.ContainsKey(key) && Keys[key] != null)
        return true;
     return false;
   }
}

Having interface ready, we write a class that defines one the process steps. This step is CreateSiteStep that creates the site collection. This class inherits from our interface IProcessStep and hence it provides the implementation of Execute and RollBack methods.


public class CreateSiteStep : IProcessStep
{
   //private method to check for preconditions for Execute method
   private bool PrequisitesPresent(ProcessStepProperties properties)
   {
      if (properties.KeyExists("SiteNumber") && properties.KeyExists("Title") && properties.KeyExists("TemplateName"))
         return true;
      return false;
   }

   public void Execute(ProcessStepProperties properties)
   {
      //Check if preconditions are met
      if (PrequisitesPresent(properties))
      {
         //Create site collection
         CreateSiteFromRequest(properties);

         //Update the property bag
         var spSite = properties.Keys["SPSite"] as SPSite;
         if (spSite != null)
         {
            properties.Keys["SiteUrl"] = spSite.Url;
         }
      }

      //Write checkpoint for rollback
      TransactionLogger.Log(this);
   }

   public void RollBack(ProcessStepProperties properties)
   {
        // Add Logging information  - ULS or event logger if appropriate
        SPSite site = properties.Keys["SPSite"] as SPSite;
        DeleteSite(site);
   }

Now there are couple of things that need to be discussed in the above code.

Firstly, as you see, I have created a private method to check pre-conditions before the Execute method is executed. This is totally upto the specific step to check if it requires any pre-conditions to be met or any values it requires before it proceeds.

Secondly, as i mentioned above I'm using ProcessProperties class (key/value collection) to transfer the state across methods and steps, I'm updating (adding) the value in the collection right after Site collection has been created. In this way, the next step (or rollback) step will have the updated information that might be useful for further execution of the process.

Thirdly and quite importantly there is a call to Log method of  TransactionLogger class. TransactionLogger.Log(this);

This is very important. Again, TransactionLogger is my custom class that I'm using to log in the information for steps that have successfully been completed. This serves the purpose of check point for me that i use to roll back when anything fails. I'm using Stack as a data structure to push the steps (of type IProcessStep) whenever any step is successfully executed and if there is any failure during the execution, i simply pop the steps and invoke RollBack method until stack is empty - hence everything is cleaned up in case of failover.

Here is the implementation of TransactionLogger class.


public class TransactionLogger
{
  //to keep checkpoints
  private static readonly Stack TransactionStack = new Stack();

  //public property to check number of steps in the stack
  public static int LogCount
  {
      get { return TransactionStack.Count; }
  }

  private IProcessStep Pull()
  {
     return TransactionStack.Pop();
  }

  //Pushes the process step into the stack
  public static void Log(IProcessStep step)
  {
      TransactionStack.Push(step);
  }

  //Pops the step from stack and invoke rollback method for every step  until stack is empty
  public static void RollBack(ProcessStepProperties properties)
  {
      while (TransactionStack.Count > 0)
      {
          var step = TransactionStack.Pop();
          step.RollBack(properties);
      }
   }
}

So, basically the idea is that in case of any failure everything is rolled back and in the implementation above, TransactionLogger class doesn't need to know the details about any ProcessStep class how to roll back because it is upto every step to roll back the things it has created. That is why TransactionLogger class adds IProcessStep object into the stack and that is our interface! here is another class called SetSitePermissionGroupStep that basically adds custom permissions to the site collection that has been created in previous process step.


public class SetSitePermissionGroupStep : IProcessStep
{

   // .... code removed for clarity purpose ....

 public override void Execute(ProcessStepProperties properties)
 {
    if (PrequisitesPresent(properties))
    {
       AddAssociatedMemberGroups(properties, _key);
       TransactionLogger.Log(this);
    }
 }

 public override void RollBack(ProcessStepProperties properties)
 {
   //Roll back implementation
 }
}

So, as a recap, we defined an interface called IProcessStep that contains the declaration of Execute and RollBack methods.

Other classes (specific to process steps) inherit from IProcessStep and provide an implementation for Execute and RollBack methods. Execute method of every process step class also add check point after successful execution of the step using TransactionLogger.Log method.

Now having all this setup we need some sort of controller class that could use this implementation. This could be used from within the workflow as well but in my demonstration I'm using a class that implements the Queue and in that queue we can insert separate steps and then Process the queue in the end. Here is the implementation of my controller class.


public class TestProcessSteps
{
    private Queue _queue = new Queue();
    private ProcessStepProperties _properties = new ProcessStepProperties();

    public void Initialize(ProcessStepProperties properties)
    {
       _properties = properties;
    }
    public void Enqueue(IProcessStep step)
    {
       this._queue.Enqueue(step);
    }

    public void ProcessQueue()
    {
      try
      {
         while (_queue.Count > 0)
         {
            //pick item from the queue and invoke execute method
            var step = _queue.Dequeue();
            step.Execute(_properties);
         }
       }
       catch (Exception ex)
       {
          //roll back
          TransactionLogger.RollBack(_properties);
       }
    }
 }

If you notice above, if anything fails (i.e, exception occurs), RollBack method is invoked from TransactionLogger class (that basically pops up items from the stack and invoke rollback method on respective step).

Finally, here is how we test our implementation.


//Instantiate TestProcessStep class
TestProcessSteps testclient = new TestProcessSteps();

//setup properties
properties = InitializeProcessProperties(item);

//Invoke Initialize method on TestProcessSteps class (that basically initializes ProcessProperties)
testclient.Initialize(properties);

//Insert steps into Queue
testclient.Enqueue(new CreateSiteStep());
testclient.Enqueue(new SetSitePermissionGroupStep("MemberGroup"));
testclient.Enqueue(new SetSitePermissionGroupStep("OwnerGroup"));

//Process the Queue (that basically takes items from queue and invoke Execute methods on respective steps)
testclient.ProcessQueue();

Note that during the enqueue, I'm adding SetSitePermissionGroupStep step two times with different parameters - in this way you can actually define atomic steps also using the same class.

Hope it gives the general idea about my thought behind the implementation. Don't hesitate to provide any feedback or issues if you find any in my solution.

Thanks for taking time to read this long post :)



comments powered by Disqus