CodeActivities für WF4 entwickeln

Im letzten Beitrag habe ich gezeigt wie man einfach eigene Activities mit dem Workflow Designer der WF4 erstellen kann. In vielen Fällen lassen sich Anforderungen bereits durch die Komposition von Activities der BAL erschlagen, doch manchmal benötigt mal eine Teilkomponente, die es in der WF4 BAL nicht gibt, oder wo die Standardimplementierung etwas von der Anforderung abweicht. Genau in diesen Szenarien sollte man sich dazu entscheiden eine eigene CodeActivity zu schreiben.

Bei der Implementierung einer CodeActivity muss die Execute Methode der Klasse CodeActivity aus dem Namespace System.Activities überschrieben werden. Diese Methode ist für die Ausführung der Activity-Logik verantwortlich und wird später von der WF4-Engine aufgerufen.

Auch zum Erstellen einer CodeActivity stellt Visual Studio 2010 ein entsprechendes Template bereit, welches den Klassenrumpf, die Beispielimplementierung der Execute Methode und ein Eingabeargument für die Activity vorgibt.

wf4_codeActivity1

 

 public sealed class VerySimpleActivity : CodeActivity
    {
        // Define an activity input argument of type string
        public InArgument<string> Text { get; set; }
 
        // If your activity returns a value, derive from CodeActivity<TResult>
        // and return the value from the Execute method.
        protected override void Execute(CodeActivityContext context)
        {
            // Obtain the runtime value of the Text input argument
            string text = context.GetValue(this.Text);
        }
    }

 

Entweder entscheidet man sich dazu die hier generierte Klasse weiter zu entwickeln, oder man geht nach TDD vor und erstellt zunächst einen Test.

Als Beispiel möchte ich eine einfache Activity erstellen, die aus einem Eingangsargument vom Typ double die Quadratwurzel ermittelt und diese wieder als double zurückgibt. Nach guter TDD Manier, zunächst der Test

     [TestMethod]
        public void Test_BerechneWurzel()
        {
            //arrange
            double input = 20;
            double expected = Math.Sqrt(input);
            var mySqrtActivity = new Sqrt();
            mySqrtActivity.NumberToProceed = input;
            
            // act
            var actual = WorkflowInvoker.Invoke(mySqrtActivity);
 
            // assert
            Assert.AreEqual<double>(expected, 
              Convert.ToDouble(actual["SqrtResult"]));
        }

 

Lässt man den Test nun laufen, wird dieser fehlschlagen. Super, dann kann die Implementierung beginnen. Damit der Test “grün” wird muss zunächst die Klasse Sqrt erstellt werden, damit eine Sqrt Instanz vom WorkflowInvoker verarbeitet werden kann muss sie von System.Activities.Activity ableiten.

CodeActivity ist eine von System.Activitites.Activity abgeleitete Klasse!

public class Sqrt : CodeActivity
{
    protected override void Execute(CodeActivityContext context)
    {
    }
 
}

 

Workflow (oder Activity) Argumente

Das nächste Implementierungsdetail ist die Property NumberToProceed, welche dafür verantwortlich ist den Ausgangswert entgegen zu nehmen, zu implementieren.

Bei der Activity-Entwicklung gibt man Argumente immer in Form der generischen Klasse InArgument<T> für Eingangsargumente bzw OutArgument<T> für Ausgangsargumente an. Wenn ein Argument sowohl Eingabe- als auch Ausgabeargument ist, verwendet man die Klasse InOutArgument<T>.

Der generische Parameter T repräsentiert hierbei den eigentlichen Typen des Argumentes.

 

In diesem konkreten Fall benötigt man

  • ein InArgument<double> für das Eingangsargument NumberToProceed
  • ein OutArgument<double> für das AusgangsArgument SqrtResult
  public class Sqrt : CodeActivity
    {
        public InArgument<double> NumberToProceed { get; set; }
        public OutArgument<double> SqrtResult { get; set; }
 
        protected override void Execute(CodeActivityContext context)
        {
        }
    }

Workflow (oder Activity) Argumente auslesen und setzen

Als letzten Schritt zum “grünen” Test, muss die Implementierung der Execute Methode vorgenommen werden. Hier muss die Quadratwurzel des Eingabeargumentes ermittelt und in das Ausgabeargument geschrieben werden.

InArgument, OutArgument und InOutArgument stellen Instanzmethoden bereit, mit dessen Hilfe die Werte des Argumentes ausgelesen und gesetzt werden können.

Argumente kann man über die Methoden Get(CodeActivityContext context) oder Get<T>(CodeActivityContext context) auslesen. Ich bevorzuge die generische Variante der Methode weil man dadurch einen Cast von Object  zum eigentlichen Typen des Argumentes sparen kann.

Den Wert eines Argumentes setzt man über die Methode Set(CodeActivityContext context, double value), der Typ des zweiten Parameters definiert sich über das generische Argument der Definition des ActivityArgumentes.

Durch Anwendung der entsprechenden Methoden, kann die Implementierung mit nur wenigen Operationen fertiggestellt werden.

public class Sqrt : CodeActivity
{
    public InArgument<double> NumberToProceed { get; set; }
    public OutArgument<double> SqrtResult { get; set; }
 
    protected override void Execute(CodeActivityContext context)
    {        
        this.SqrtResult.Set(context, Math.Sqrt(NumberToProceed.Get<double>(context)));
     }
}

 

Der zu Beginn geschriebene Test läuft nun erfolgreich durch, daher kann man sicherstellen, dass die erste eigene Activity komplett ist und allen Anforderungen entspricht.

 

Wenn es sein muss, dann muss es sein!

Sehr oft hat man Argumente für Activities, die zwingend angegeben werden müssen um das korrekte Laufzeitverhalten der Activity zu garantieren. Auch hierfür hat die WF4 Engine eine Lösung zur Hand, das RequiredArgument  aus dem Namespace System.Activities, dekoriert man ein InArgument oder ein InOutArgument mit diesem Attribut, so muss dieses Argument zwingend angegeben werden.

Gibt der Anwender dieses Argument nicht an, akzeptiert die WF4 Engine das XAML bzw. die Activity nicht und generiert die entsprechende Exception. Im WF4-Designer werden Activities, die nicht komplett konfiguriert wurden durch ein entsprechendes Symbol gekennzeichnet.

wf_code_activity2

 

Die eigene Assembly verwenden

Durch einen einfachen Kompilierungsvorgang (CTRL + SHIFT + B), ist die eigene Activity fertig und kann in anderen Workflows verwendet werden, sofern diese eine Referenz auf die Activity-Assembly haben.

Die eigenen Activities werden auch automatisch in der Toolbox von Visual Studio 2010 angezeigt und können somit einfach via Drag’n’Drop im WF4-Designer verwendet werden.

 

wf4_codeActivity2

 

Die generische Variante

Neben der hier gezeigten Möglichkeit von System.Activities.CodeActivity abzuleiten, kann man auch von der generischen Variante CodeActivity<T> ableiten, der Vorteil bei der Ableitung von CodeActivity<T> ist, dass die Signatur der Execute Methode direkt einen entsprechend typisierten Rückgabewert hat.

Darüber hinaus verfügt eine von CodeActivity<T> abgeleitete Klasse direkt über ein OutArgument<T> Result, welches zur Speicherung des Rückgabewertes verwendet werden kann.

 

Technorati-Tags: ,,,
DotNetKicks-DE Image
Published Mittwoch, 17. März 2010 12:08 von ThorstenHans
Abgelegt unter: ,

Kommentare

Keine Kommentare

Kommentar abgeben

(verpflichtend) 
(verpflichtend) 
(optional)
(verpflichtend)