Ich hab' zu Demo-Zwecken ein Beispiel erstellt, welches viele Aspekte der Implementierung von Workflow-Aktivitäten für WF 4 durch Ableiten von NativeActivity aufzeigt. Ich nenne diese Aktivität CountingWhile, weil sie eine While-Schleife darstellt, welche einen Zähler hochzählt (der aber nichts mit der Bedingung der Schleife zu tun hat).
Im Konstruktor wird der Body (= die Sub-Aktivität) instanziert. Im Zuge dessen wird definiert, dass der int , der an die Sub-Aktivität weitergereicht wird, über den Variablennamen PassedIndex angesprochen werden kann. In CacheMetadata werden die einzelnen Variablen, Argumente und Subaktivitäten bei der Workflow-Runtime registriert (für Argumente werden Instanzen von RuntimeArgument registriert und mit den Argumenten via Bind verbunden). Über Execute erfolgen einige verschachtelte asynchrone Aufrufe. Das folgende Listing beinhaltet den gesamten Quellcode. Das zweite Listing beinhaltet den Kern des dazugehörigen ActivityDesigners, auf den in der ersten Zeile im ersten Listing verwiesen wird.
Es wird in ein paar Wochen von mir eine Einführung über WF 4 im .Net-Magazin geben, wo ich diese Dinge und weitere etwas ausführlicher erklären werde.
[Designer(typeof(CountingWhileDesigner))]
public class CountingWhile : NativeActivity
{
[Browsable(false)]
public ActivityAction<int> Body { get; set; }
public Activity<bool> Condition { get; set; }
private Variable<int> CurrentIndex = new Variable<int>("CurrentIndex");
public InArgument<int> StartIndex { get; set; }
public OutArgument<int> Index { get; set; }
public CountingWhile()
{
this.Body = new ActivityAction<int>
{
Argument = new DelegateInArgument<int>
{
Name="PassedIndex"
}
};
}
protected override void CacheMetadata(NativeActivityMetadata metadata)
{
metadata.AddImplementationVariable(CurrentIndex);
RuntimeArgument arg = new RuntimeArgument("StartIndex", typeof(int), ArgumentDirection.In);
metadata.Bind(StartIndex, arg);
metadata.AddArgument(arg);
RuntimeArgument argOut = new RuntimeArgument("Index", typeof(int), ArgumentDirection.Out);
metadata.Bind(Index, argOut);
metadata.AddArgument(argOut);
metadata.AddDelegate(Body);
metadata.AddChild(Condition);
}
protected override void Execute(NativeActivityContext context)
{
int start = StartIndex.Get(context);
CurrentIndex.Set(context, start);
BeginIteration(context);
}
private void BeginIteration(NativeActivityContext context)
{
int current = CurrentIndex.Get(context);
Index.Set(context, current);
context.ScheduleActivity<bool>(
Condition,
OnChecked,
OnFault);
}
private void OnChecked(NativeActivityContext context, ActivityInstance instance, bool result)
{
if (result )
{
int current = CurrentIndex.Get(context);
context.ScheduleAction<int>(
Body,
current,
OnBodyComplete,
OnFault);
}
}
private void OnBodyComplete(NativeActivityContext context, ActivityInstance instance)
{
CurrentIndex.Set(context, CurrentIndex.Get(context) + 1);
BeginIteration(context);
}
private void OnFault(NativeActivityFaultContext context, Exception ex, ActivityInstance instance)
{
CurrentIndex.Set(context, CurrentIndex.Get(context) + 1);
//context.HandleFault();
}
}
<sap:ActivityDesigner x:Class="ActivityDesignerLibrary.CountingWhileDesigner" [...]>
<sap:WorkflowItemPresenter MinHeight="50" Item="{Binding Path=ModelItem.Body.Handler, Mode=TwoWay}" HintText="Add body here" />
</sap:ActivityDesigner>