Freigeben über


Verwenden von WorkflowInvoker und WorkflowApplication

Windows Workflow Foundation (WF) bietet mehrere Methoden zum Hosten von Workflows. WorkflowInvoker bietet eine einfache Möglichkeit zum Aufrufen eines Workflows, als wäre es ein Methodenaufruf und kann nur für Workflows verwendet werden, die keine Persistenz verwenden. WorkflowApplication stellt ein umfangreicheres Modell zum Ausführen von Workflows bereit, das Benachrichtigungen über Lebenszyklusereignisse, Ausführungskontrolle, Wiederaufnahme von Lesezeichen und Persistenz umfasst. WorkflowServiceHost bietet Unterstützung für Messagingaktivitäten und wird in erster Linie mit Workflowdiensten verwendet. Dieses Thema führt Sie in das Workflow-Hosting mit WorkflowInvoker und WorkflowApplication ein. Weitere Informationen zum Hosten von Workflows mit WorkflowServiceHost, finden Sie unter Workflowdienste und Hosting-Workflowdienste (Übersicht).

Verwenden von WorkflowInvoker

WorkflowInvoker stellt ein Modell zum Ausführen eines Workflows bereit, als wäre es ein Methodenaufruf. Um einen Workflow mit WorkflowInvoker zu starten, verwenden Sie die Invoke Methode und übergeben die Definition des zu startenden Workflows. In diesem Beispiel wird eine Aktivität WriteLine mithilfe von WorkflowInvoker aufgerufen.

Activity wf = new WriteLine
{
    Text = "Hello World."
};

WorkflowInvoker.Invoke(wf);

Wenn ein Workflow mithilfe WorkflowInvokeraufgerufen wird, wird der Workflow im aufrufenden Thread ausgeführt, und die Invoke Methode blockiert, bis der Workflow abgeschlossen ist, einschließlich aller Leerlaufzeiten. Um ein anderes Timeoutintervall für den Abschluss des Workflows zu konfigurieren, verwenden Sie eine der Invoke-Überladungen, die einen TimeSpan-Parameter annehmen. In diesem Beispiel wird ein Workflow zweimal mit zwei verschiedenen Timeoutintervallen aufgerufen. Der erste Workflow wird abgeschlossen, der zweite jedoch nicht.

Activity wf = new Sequence()
{
    Activities =
    {
        new WriteLine()
        {
            Text = "Before the 1 minute delay."
        },
        new Delay()
        {
            Duration = TimeSpan.FromMinutes(1)
        },
        new WriteLine()
        {
            Text = "After the 1 minute delay."
        }
    }
};

// This workflow completes successfully.
WorkflowInvoker.Invoke(wf, TimeSpan.FromMinutes(2));

// This workflow does not complete and a TimeoutException
// is thrown.
try
{
    WorkflowInvoker.Invoke(wf, TimeSpan.FromSeconds(30));
}
catch (TimeoutException ex)
{
    Console.WriteLine(ex.Message);
}

Hinweis

Dies TimeoutException wird nur ausgelöst, wenn das Timeoutintervall verstrichen ist und der Workflow während der Ausführung leer ist. Ein Workflow, der länger als das angegebene Timeout-Intervall dauert, wird erfolgreich abgeschlossen, wenn der Workflow nicht in den Leerlauf kommt.

WorkflowInvoker stellt außerdem asynchrone Versionen der Aufrufmethode bereit. Weitere Informationen finden Sie unter InvokeAsync und BeginInvoke.

Festlegen von Eingabeargumenten eines Workflows

Daten können mithilfe eines Wörterbuchs mit Eingabeparametern an einen Workflow übergeben werden, die nach Argumentnamen geordnet sind und den Eingabeargumenten des Workflows entsprechen. In diesem Beispiel wird ein WriteLine Aufruf ausgeführt, und der Wert für sein Text Argument wird mithilfe des Wörterbuchs der Eingabeparameter angegeben.

Activity wf = new WriteLine();

Dictionary<string, object> inputs = new Dictionary<string, object>();
inputs.Add("Text", "Hello World.");

WorkflowInvoker.Invoke(wf, inputs);

Abrufen von Ausgabeargumenten eines Workflows

Die Ausgabeparameter eines Workflows können mithilfe des Ausgabewörterbuchs abgerufen werden, das vom Aufruf an Invokezurückgegeben wird. Im folgenden Beispiel wird ein Workflow aufgerufen, der aus einer einzelnen Divide Aktivität besteht, die zwei Eingabeargumente und zwei Ausgabeargumente enthält. Wenn der Workflow aufgerufen wird, wird das arguments Wörterbuch übergeben, das die Werte für jedes Eingabeargument enthält und anhand des Argumentnamens ordnet. Wenn der Aufruf von Invoke zurückgegeben wird, werden die einzelnen Ausgabeargumente nach Argumentname sortiert im outputs-Wörterbuch zurückgegeben.

public sealed class Divide : CodeActivity
{
    [RequiredArgument]
    public InArgument<int> Dividend { get; set; }

    [RequiredArgument]
    public InArgument<int> Divisor { get; set; }

    public OutArgument<int> Remainder { get; set; }
    public OutArgument<int> Result { get; set; }

    protected override void Execute(CodeActivityContext context)
    {
        int quotient = Dividend.Get(context) / Divisor.Get(context);
        int remainder = Dividend.Get(context) % Divisor.Get(context);

        Result.Set(context, quotient);
        Remainder.Set(context, remainder);
    }
}
int dividend = 500;
int divisor = 36;

Dictionary<string, object> arguments = new Dictionary<string, object>();
arguments.Add("Dividend", dividend);
arguments.Add("Divisor", divisor);

IDictionary<string, object> outputs =
    WorkflowInvoker.Invoke(new Divide(), arguments);

Console.WriteLine($"{dividend} / {divisor} = {outputs["Result"]} Remainder {outputs["Remainder"]}");

Wenn der Workflow von ActivityWithResult, zum Beispiel CodeActivity<TResult> oder Activity<TResult>, abgeleitet wird und neben dem gut definierten Ausgabeargument Result zusätzliche Ausgabeargumente vorhanden sind, muss eine nicht generische Überladung von Invoke verwendet werden, um die zusätzlichen Argumente abzurufen. Dazu muss die an Invoke übergebene Workflowdefinition vom Typ Activity sein. In diesem Beispiel wird die Divide-Aktivität von CodeActivity<int> abgeleitet, aber als Activity deklariert, damit eine nicht generische Überladung von Invoke verwendet wird, die ein Wörterbuch von Argumenten anstelle eines einzelnen Rückgabewerts zurückgibt.

public sealed class Divide : CodeActivity<int>
{
    public InArgument<int> Dividend { get; set; }
    public InArgument<int> Divisor { get; set; }
    public OutArgument<int> Remainder { get; set; }

    protected override int Execute(CodeActivityContext context)
    {
        int quotient = Dividend.Get(context) / Divisor.Get(context);
        int remainder = Dividend.Get(context) % Divisor.Get(context);

        Remainder.Set(context, remainder);

        return quotient;
    }
}
int dividend = 500;
int divisor = 36;

Dictionary<string, object> arguments = new Dictionary<string, object>();
arguments.Add("Dividend", dividend);
arguments.Add("Divisor", divisor);

Activity wf = new Divide();

IDictionary<string, object> outputs =
    WorkflowInvoker.Invoke(wf, arguments);

Console.WriteLine($"{dividend} / {divisor} = {outputs["Result"]} Remainder {outputs["Remainder"]}");

Verwenden von WorkflowApplication

WorkflowApplication bietet eine vielzahl von Features für die Workflowinstanzverwaltung. WorkflowApplication dient als threadsicherer Proxy für den tatsächlichen WorkflowInstanceProxy, der die Laufzeit kapselt, und bietet Methoden zum Erstellen und Laden von Workflowinstanzen, Anhalten und Fortsetzen, Beenden und Benachrichtigung von Lebenszyklusereignissen. Um einen Workflow mit WorkflowApplication zu erstellen, abonnieren Sie WorkflowApplication alle gewünschten Lebenszyklusereignisse, starten Sie den Workflow und warten Sie dann, bis er abgeschlossen ist. In diesem Beispiel wird eine Workflowdefinition erstellt, die aus einer WriteLine-Aktivität besteht, und mit der angegebenen Workflowdefinition wird ein WorkflowApplication erstellt. Completed wird behandelt, damit der Host benachrichtigt wird, wenn der Workflow abgeschlossen ist, der Workflow mit einem Aufruf Rungestartet wird, und dann wartet der Host, bis der Workflow abgeschlossen ist. Wenn der Workflow abgeschlossen ist, wird der AutoResetEvent festgelegt, und die Hostanwendung kann die Ausführung fortsetzen, wie im folgenden Beispiel gezeigt.

AutoResetEvent syncEvent = new AutoResetEvent(false);

Activity wf = new WriteLine
{
    Text = "Hello World."
};

// Create the WorkflowApplication using the desired
// workflow definition.
WorkflowApplication wfApp = new WorkflowApplication(wf);

// Handle the desired lifecycle events.
wfApp.Completed = delegate (WorkflowApplicationCompletedEventArgs e)
{
    syncEvent.Set();
};

// Start the workflow.
wfApp.Run();

// Wait for Completed to arrive and signal that
// the workflow is complete.
syncEvent.WaitOne();

WorkflowApplication-Lifecycle-Ereignisse

Zusätzlich zum Fall Completed können Hostautoren benachrichtigt werden, wenn ein Workflow entladen wird (Unloaded), abgebrochen wurde (Aborted), in den Leerlauf wechselt (Idle und PersistableIdle) oder eine nicht behandelte Ausnahme auftritt (OnUnhandledException). Workflowanwendungsentwickler können diese Benachrichtigungen behandeln und entsprechende Maßnahmen ergreifen, wie im folgenden Beispiel gezeigt.

wfApp.Completed = delegate (WorkflowApplicationCompletedEventArgs e)
{
    if (e.CompletionState == ActivityInstanceState.Faulted)
    {
        Console.WriteLine($"Workflow {e.InstanceId} Terminated.");
        Console.WriteLine($"Exception: {e.TerminationException.GetType().FullName}\n{e.TerminationException.Message}");
    }
    else if (e.CompletionState == ActivityInstanceState.Canceled)
    {
        Console.WriteLine($"Workflow {e.InstanceId} Canceled.");
    }
    else
    {
        Console.WriteLine($"Workflow {e.InstanceId} Completed.");

        // Outputs can be retrieved from the Outputs dictionary,
        // keyed by argument name.
        // Console.WriteLine($"The winner is {e.Outputs["Winner"]}.");
    }
};

wfApp.Aborted = delegate (WorkflowApplicationAbortedEventArgs e)
{
    // Display the exception that caused the workflow
    // to abort.
    Console.WriteLine($"Workflow {e.InstanceId} Aborted.");
    Console.WriteLine($"Exception: {e.Reason.GetType().FullName}\n{e.Reason.Message}");
};

wfApp.Idle = delegate (WorkflowApplicationIdleEventArgs e)
{
    // Perform any processing that should occur
    // when a workflow goes idle. If the workflow can persist,
    // both Idle and PersistableIdle are called in that order.
    Console.WriteLine($"Workflow {e.InstanceId} Idle.");
};

wfApp.PersistableIdle = delegate (WorkflowApplicationIdleEventArgs e)
{
    // Instruct the runtime to persist and unload the workflow.
    // Choices are None, Persist, and Unload.
    return PersistableIdleAction.Unload;
};

wfApp.Unloaded = delegate (WorkflowApplicationEventArgs e)
{
    Console.WriteLine($"Workflow {e.InstanceId} Unloaded.");
};

wfApp.OnUnhandledException = delegate (WorkflowApplicationUnhandledExceptionEventArgs e)
{
    // Display the unhandled exception.
    Console.WriteLine($"OnUnhandledException in Workflow {e.InstanceId}\n{e.UnhandledException.Message}");

    Console.WriteLine($"ExceptionSource: {e.ExceptionSource.DisplayName} - {e.ExceptionSourceInstanceId}");

    // Instruct the runtime to terminate the workflow.
    // Other choices are Abort and Cancel. Terminate
    // is the default if no OnUnhandledException handler
    // is present.
    return UnhandledExceptionAction.Terminate;
};

Festlegen von Eingabeargumenten eines Workflows

Daten können beim Start eines Workflows mithilfe eines Wörterbuchs mit Parametern in den Workflow übergeben werden. Diese Vorgehensweise ähnelt dem Übergeben von Daten bei der Verwendung des WorkflowInvoker-Objekts. Jedes Element im Wörterbuch wird einem Eingabeargument des angegebenen Workflows zugeordnet. In diesem Beispiel wird ein Workflow, der aus einer WriteLine Aktivität besteht, aufgerufen, und sein Text Argument wird mithilfe des Wörterbuchs der Eingabeparameter angegeben.

AutoResetEvent syncEvent = new AutoResetEvent(false);

Activity wf = new WriteLine();

// Create the dictionary of input parameters.
Dictionary<string, object> inputs = new Dictionary<string, object>();
inputs.Add("Text", "Hello World!");

// Create the WorkflowApplication using the desired
// workflow definition and dictionary of input parameters.
WorkflowApplication wfApp = new WorkflowApplication(wf, inputs);

// Handle the desired lifecycle events.
wfApp.Completed = delegate (WorkflowApplicationCompletedEventArgs e)
{
    syncEvent.Set();
};

// Start the workflow.
wfApp.Run();

// Wait for Completed to arrive and signal that
// the workflow is complete.
syncEvent.WaitOne();

Abrufen von Ausgabeargumenten eines Workflows

Wenn ein Workflow abgeschlossen ist, können alle Ausgabeargumente im Completed Handler abgerufen werden, indem sie auf das WorkflowApplicationCompletedEventArgs.Outputs Wörterbuch zugreifen. Im folgenden Beispiel wird ein Workflow mit WorkflowApplication gehostet. Eine WorkflowApplication Instanz wird mithilfe einer Workflowdefinition erstellt, die aus einer einzelnen DiceRoll Aktivität besteht. Die DiceRoll Aktivität weist zwei Ausgabeargumente auf, die die Ergebnisse des Würfelrollvorgangs darstellen. Nach Abschluss des Workflows werden die Ausgaben im Completed Handler abgerufen.

public sealed class DiceRoll : CodeActivity
{
    public OutArgument<int> D1 { get; set; }
    public OutArgument<int> D2 { get; set; }

    static Random r = new Random();

    protected override void Execute(CodeActivityContext context)
    {
        D1.Set(context, r.Next(1, 7));
        D2.Set(context, r.Next(1, 7));
    }
}
// Create a WorkflowApplication instance.
WorkflowApplication wfApp = new WorkflowApplication(new DiceRoll());

// Subscribe to any desired workflow lifecycle events.
wfApp.Completed = delegate (WorkflowApplicationCompletedEventArgs e)
{
    if (e.CompletionState == ActivityInstanceState.Faulted)
    {
        Console.WriteLine($"Workflow {e.InstanceId} Terminated.");
        Console.WriteLine($"Exception: {e.TerminationException.GetType().FullName}\n{e.TerminationException.Message}");
    }
    else if (e.CompletionState == ActivityInstanceState.Canceled)
    {
        Console.WriteLine($"Workflow {e.InstanceId} Canceled.");
    }
    else
    {
        Console.WriteLine($"Workflow {e.InstanceId} Completed.");

        // Outputs can be retrieved from the Outputs dictionary,
        // keyed by argument name.
        Console.WriteLine($"The two dice are {e.Outputs["D1"]} and {e.Outputs["D2"]}.");
    }
};

// Run the workflow.
wfApp.Run();

Hinweis

WorkflowApplication und WorkflowInvoker nehmen ein Wörterbuch von Eingabeargumenten und geben ein Wörterbuch von out Argumenten zurück. Diese Wörterbuchparameter, Eigenschaften und Rückgabewerte sind vom Typ IDictionary<string, object>. Die tatsächliche Instanz der Wörterbuchklasse, die übergeben wird, kann jede Klasse sein, die IDictionary<string, object> implementiert. In diesen Beispielen Dictionary<string, object> wird verwendet. Weitere Informationen zu Wörterbüchern finden Sie unter IDictionary<TKey,TValue> und Dictionary<TKey,TValue>.

Übergabe von Daten in einen laufenden Workflow mithilfe von Textmarken

Lesezeichen sind der Mechanismus, mit dem eine Aktivität passiv auf die Fortsetzung warten kann und ein Mechanismus zum Übergeben von Daten in eine ausgeführte Workflowinstanz ist. Wenn eine Aktivität auf Daten wartet, kann sie ein Bookmark erstellen und eine Callback-Methode registrieren, die beim Fortsetzen des Bookmark aufgerufen werden soll, wie im folgenden Beispiel gezeigt.

public sealed class ReadLine : NativeActivity<string>
{
    [RequiredArgument]
    public InArgument<string> BookmarkName { get; set; }

    protected override void Execute(NativeActivityContext context)
    {
        // Create a Bookmark and wait for it to be resumed.
        context.CreateBookmark(BookmarkName.Get(context),
            new BookmarkCallback(OnResumeBookmark));
    }

    // NativeActivity derived activities that do asynchronous operations by calling
    // one of the CreateBookmark overloads defined on System.Activities.NativeActivityContext
    // must override the CanInduceIdle property and return true.
    protected override bool CanInduceIdle
    {
        get { return true; }
    }

    public void OnResumeBookmark(NativeActivityContext context, Bookmark bookmark, object obj)
    {
        // When the Bookmark is resumed, assign its value to
        // the Result argument.
        Result.Set(context, (string)obj);
    }

Bei Ausführung erstellt die ReadLine-Aktivität ein Bookmark, registriert einen Rückruf und wartet dann darauf, dass die Bookmark-Aktivität fortgesetzt wird. Wenn sie fortgesetzt wird, weist die ReadLine-Aktivität die Daten, die mit dem Bookmark übergeben wurden, ihrem Result-Argument zu. In diesem Beispiel wird ein Workflow erstellt, der die ReadLine Aktivität verwendet, um den Namen des Benutzers zu erfassen und im Konsolenfenster anzuzeigen.

Variable<string> name = new Variable<string>();

Activity wf = new Sequence
{
    Variables = { name },
    Activities =
     {
         new WriteLine
         {
             Text = "What is your name?"
         },
         new ReadLine
         {
             BookmarkName = "UserName",
             Result = new OutArgument<string>(name)
         },
         new WriteLine
         {
             Text = new InArgument<string>((env) =>
                 ("Hello, " + name.Get(env)))
         }
     }
};

// Create a WorkflowApplication instance.
WorkflowApplication wfApp = new WorkflowApplication(wf);

// Workflow lifecycle events omitted except idle.
AutoResetEvent idleEvent = new AutoResetEvent(false);

wfApp.Idle = delegate (WorkflowApplicationIdleEventArgs e)
{
    idleEvent.Set();
};

// Run the workflow.
wfApp.Run();

// Wait for the workflow to go idle before gathering
// the user's input.
idleEvent.WaitOne();

// Gather the user's input and resume the bookmark.
// Bookmark resumption only occurs when the workflow
// is idle. If a call to ResumeBookmark is made and the workflow
// is not idle, ResumeBookmark blocks until the workflow becomes
// idle before resuming the bookmark.
BookmarkResumptionResult result = wfApp.ResumeBookmark("UserName",
    Console.ReadLine());

// Possible BookmarkResumptionResult values:
// Success, NotFound, or NotReady
Console.WriteLine($"BookmarkResumptionResult: {result}");

Wenn die ReadLine-Aktivität ausgeführt wird, erstellt sie ein Bookmark-Objekt mit dem Namen UserName und wartet dann darauf, dass das Lesezeichen wieder aufgenommen wird. Der Host sammelt die gewünschten Daten und setzt dann das Bookmark fort. Der Workflow wird fortgesetzt, zeigt den Namen an und wird dann abgeschlossen.

Die Hostanwendung kann den Workflow überprüfen, um festzustellen, ob aktive Lesezeichen vorhanden sind. Dies kann durch Aufrufen der GetBookmarks-Methode einer WorkflowApplication-Instanz oder durch Überprüfung von WorkflowApplicationIdleEventArgs im Idle-Handler ausgeführt werden.

Das nachstehende Codebeispiel ähnelt dem vorherigen Beispiel, mit der Ausnahme, dass die aktiven Bookmarks vor dem Fortsetzen des Bookmarks aufgezählt werden. Der Workflow wird gestartet, und sobald Bookmark erstellt wurde und der Workflow in den Leerlauf wechselt, wird GetBookmarks aufgerufen. Nach Abschluss des Workflows wird die folgende Ausgabe in der Konsole angezeigt.

Wie heißen Sie?
BookmarkName: UserName - OwnerDisplayName: ReadLineSteveHello, Steve

Variable<string> name = new Variable<string>();

Activity wf = new Sequence
{
    Variables = { name },
    Activities =
     {
         new WriteLine
         {
             Text = "What is your name?"
         },
         new ReadLine
         {
             BookmarkName = "UserName",
             Result = new OutArgument<string>(name)
         },
         new WriteLine
         {
             Text = new InArgument<string>((env) =>
                 ("Hello, " + name.Get(env)))
         }
     }
};

// Create a WorkflowApplication instance.
WorkflowApplication wfApp = new WorkflowApplication(wf);

// Workflow lifecycle events omitted except idle.
AutoResetEvent idleEvent = new AutoResetEvent(false);

wfApp.Idle = delegate (WorkflowApplicationIdleEventArgs e)
{
    // You can also inspect the bookmarks from the Idle handler
    // using e.Bookmarks

    idleEvent.Set();
};

// Run the workflow.
wfApp.Run();

// Wait for the workflow to go idle and give it a chance
// to create the Bookmark.
idleEvent.WaitOne();

// Inspect the bookmarks
foreach (BookmarkInfo info in wfApp.GetBookmarks())
{
    Console.WriteLine($"BookmarkName: {info.BookmarkName} - OwnerDisplayName: {info.OwnerDisplayName}");
}

// Gather the user's input and resume the bookmark.
wfApp.ResumeBookmark("UserName", Console.ReadLine());

Im folgenden Codebeispiel werden die in den WorkflowApplicationIdleEventArgs-Handler einer Idle-Instanz übergebenen WorkflowApplication überprüft. In diesem Beispiel wird der Workflow, der in den Ruhezustand versetzt wird, von einer Aktivität namens Bookmark betrieben und enthält ein EnterGuess mit dem Namen ReadInt. Dieses Codebeispiel basiert auf How to: Run a Workflow, das Teil des Getting Started Tutorial ist. Wenn der Idle Handler in diesem Schritt so geändert wird, dass er den Code aus diesem Beispiel enthält, wird die folgende Ausgabe angezeigt.

BookmarkName: EnterGuess - OwnerDisplayName: ReadInt

wfApp.Idle = delegate (WorkflowApplicationIdleEventArgs e)
{
    foreach (BookmarkInfo info in e.Bookmarks)
    {
        Console.WriteLine($"BookmarkName: {info.BookmarkName} - OwnerDisplayName: {info.OwnerDisplayName}");
    }

    idleEvent.Set();
};

Zusammenfassung

WorkflowInvoker bietet eine einfache Möglichkeit zum Aufrufen von Workflows. Obwohl es Methoden zum Übergeben von Daten zu Beginn eines Workflows und zum Extrahieren von Daten aus einem abgeschlossenen Workflow bereitstellt, ist es nicht für komplexere Szenarien gedacht; hierfür kann WorkflowApplication verwendet werden.