Navigation and service panel


Content

This text is fallbacked from the German Version. If you need use Google Translate


Pipeline mit Rollback Funktionalität

By Kevin Brechbühl on 31. January 2013, No comments

Mit den Pipelines bietet Sitecore einen sehr einfachen und eleganten Weg, mehrere gekapselte Aktionen nacheinandern auszuführen. Was allerdings fehlt, ist eine Transaktion über die einzelnen Prozessoren. Das heisst, wenn in einem Pipeline Prozessor ein Fehler passiert, wird die Pipeline unterbrochen und nicht mehr weiter ausgeführt. Bis dahin ausgeführte Prozessoren und Aktionen können jedoch nicht rückgängig gemacht werden. Um dies zu implementieren eignet sich das Reversible Command Pattern, welches in diesem Beitrag erläutert wird.

Die Pipelines von Sitecore sind sehr generisch und ebenfalls sehr flexibel gehalten. Manchmal gibt es jedoch einen Anwendungsfall, welcher ein Rollback von bereits ausgeführten Aktionen ausführen muss, wenn ein Fehler auftritt. Ein Beispiel könnte ein Kontaktformular sein, welches nach dem Postback folgende Aktionen ausführen muss:

  1. Daten in Datenbank speichern
  2. Benutzer an Newsletter anmelden
  3. E-Mail an Kunden sowie an den Kundenservice schicken

Wenn nun das Versenden des E-Mails fehlschlägt, so soll dem Benutzer eine Fehlermeldung angezeigt werden und die bereits in die Datenbank geschriebenen Daten sowie die Newsletteranmeldung wieder rückgängig gemacht werden. Dieser Anwendungsfall wäre prädestiniert für eine eigene Pipeline, wäre da nicht das Rollback Szenario.

Um ebenfalls mehrere Aktionen ausführen zu können, eignet sich das Command Design Pattern. Dieses Pattern eignet sich dazu, eigene Aktionen zu kapseln und diese auch auszuführen. Mit einer kleinen Modifikation resp. Erweiterungen gibt es daraus das Reversible Command Pattern.

Eine einfache Implementation eines Commands hat zwei Methoden:

public interface ICommand
{
 bool Execute();
 void Reverse();
}

Der CommandInvoker speichert die einzelnen Commands und führt diese dann aus. Bei einem Fehler eines Commands führt er für jeden bereits ausgeführten Command die Reverse() Methode aus. Vereinfacht sieht der Invoker wie folgt aus:

public class CommandInvoker
{
    private SynchronizedCollection<ICommand> commands = new SynchronizedCollection<ICommand>();

    private Stack<ICommand> executedCommands;

    public void AddCommand(ICommand command)
    {
        this.commands.Add(command);
    }

    public bool Execute()
    {
        Monitor.Enter(this.commands.SyncRoot);
        try
        {
            if (this.commands.Count > 0)
            {
                this.executedCommands = new Stack<ICommand>();
                foreach (ICommand command in this.commands)
                {
                    this.executedCommands.Push(command);
                    if (!command.Execute())
                    {
                        throw new Exception(string.Format("Error while processing command '{0}'", command.ToString()));
                    }
                }
            }
            return true;
        }
        catch (Exception ex)
        {
            this.Rollback();
            return false;
        }
        finally
        {
            Monitor.Exit(this.commands.SyncRoot);
        }
    }

    private void Rollback()
    {
        while (this.executedCommands.Count > 0)
        {
            ICommand command = this.executedCommands.Pop();
            command.Reverse();
        }
    }
}

Eine konkrete Implementation eines Commands könnte so aussehen:

public class TestCommand : ICommand
{
    private string test;

    public TestCommand(string test)
    {
        this.test = test;
    }

    public bool Execute()
    {
        Sitecore.Diagnostics.Log.Info(string.Format("Test: {0}", this.test), this);
        return true;
    }

    public void Reverse()
    {
        // do revert here
    }
}

Und der Aufruf des Invokers:

var invoker = new CommandInvoker();
invoker.AddCommand(new TestCommand("My Command"));
invoker.Execute();

Der Nachteil gegenüber den Pipelines ist, dass diese Methode etwas weniger generisch ist und dass die einzelnen Commands nicht via Configfile konfiguriert werden können. Um dies trotzdem zu erreichen könnte man auch die CorePipeline von Sitecore um entsprechenden Funktionalitäten erweitern. Wem die Implementation mit dem Reversible Command Pattern reicht, kann die beiden vollständigen Klassen hier herunterladen.

No comments

Add your comment

Your email address will not be published. Required fields are marked *

*