Udostępnij za pośrednictwem


Używanie obiektu WorkflowInvoker i WorkflowApplication

Program Windows Workflow Foundation (WF) udostępnia kilka metod hostowania przepływów pracy. WorkflowInvoker Zapewnia prosty sposób wywoływania przepływu pracy tak, jakby był to wywołanie metody i może być używany tylko w przypadku przepływów pracy, które nie używają trwałości. WorkflowApplication Udostępnia bogatszy model wykonywania przepływów pracy, który obejmuje powiadomienia o zdarzeniach cyklu życia, kontrolę wykonywania, wznowienie zakładek i trwałość. WorkflowServiceHost zapewnia obsługę działań związanych z obsługą komunikatów i jest używany głównie z usługami przepływu pracy. W tym temacie przedstawiono hostowanie przepływów pracy za pomocą WorkflowInvoker poleceń i WorkflowApplication. Aby uzyskać więcej informacji na temat hostowania przepływów pracy za pomocą WorkflowServiceHostprogramu , zobacz Omówienie usług przepływu pracy i hostingu usług przepływu pracy.

Korzystanie z elementu WorkflowInvoker

WorkflowInvoker udostępnia model do wykonywania przepływu pracy tak, jakby był to wywołanie metody. Aby wywołać przepływ pracy przy użyciu metody WorkflowInvoker, wywołaj Invoke metodę i przekaż definicję przepływu pracy przepływu pracy do wywołania. W tym przykładzie działanie WriteLine jest wywoływane przy użyciu polecenia WorkflowInvoker.

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

WorkflowInvoker.Invoke(wf);

Gdy przepływ pracy jest wywoływany przy użyciu WorkflowInvokermetody , przepływ pracy jest wykonywany w wątku wywołującym i Invoke blokuje metodę do momentu zakończenia przepływu pracy, w tym dowolnego czasu bezczynności. Aby skonfigurować interwał limitu czasu, w którym przepływ pracy musi zostać ukończony, użyj jednego z Invoke przeciążeń, które przyjmuje TimeSpan parametr. W tym przykładzie przepływ pracy jest wywoływany dwa razy z dwoma różnymi interwałami limitu czasu. Pierwszy przepływ pracy zostanie ukończony, ale drugi nie.

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);
}

Uwaga

Parametr TimeoutException jest zgłaszany tylko wtedy, gdy interwał limitu czasu upłynął, a przepływ pracy stanie się bezczynny podczas wykonywania. Przepływ pracy, który trwa dłużej niż określony interwał przekroczenia limitu czasu, zostanie ukończony pomyślnie, jeśli przepływ pracy nie stanie się bezczynny.

WorkflowInvoker Udostępnia również asynchroniczne wersje metody invoke. Aby uzyskać więcej informacji, zobacz tematy InvokeAsync oraz BeginInvoke.

Ustawianie argumentów wejściowych przepływu pracy

Dane mogą być przekazywane do przepływu pracy przy użyciu słownika parametrów wejściowych, z kluczem według nazwy argumentu, które są mapowane na argumenty wejściowe przepływu pracy. W tym przykładzie WriteLine wywoływana jest wartość , a wartość argumentu Text jest określana przy użyciu słownika parametrów wejściowych.

Activity wf = new WriteLine();

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

WorkflowInvoker.Invoke(wf, inputs);

Pobieranie argumentów wyjściowych przepływu pracy

Parametry wyjściowe przepływu pracy można uzyskać przy użyciu słownika outputs zwracanego z wywołania do Invokemetody . Poniższy przykład wywołuje przepływ pracy składający się z jednego Divide działania, który ma dwa argumenty wejściowe i dwa argumenty wyjściowe. Po wywołaniu przepływu pracy słownik jest przekazywany, arguments który zawiera wartości dla każdego argumentu wejściowego, kluczem według nazwy argumentu. Gdy wywołanie funkcji zwraca Invoke , każdy argument wyjściowy jest zwracany w słowniku outputs , również kluczem według nazwy argumentu.

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("{0} / {1} = {2} Remainder {3}",
    dividend, divisor, outputs["Result"], outputs["Remainder"]);

Jeśli przepływ pracy pochodzi z ActivityWithResultelementu , takiego jak CodeActivity<TResult> lub Activity<TResult>, i istnieją argumenty wyjściowe oprócz dobrze zdefiniowanego Result argumentu wyjściowego, aby pobrać dodatkowe argumenty, należy użyć przeciążenia innego niż Invoke ogólny. Aby to zrobić, definicja przepływu pracy przekazana Invoke do musi być typu Activity. W tym przykładzie Divide działanie pochodzi z CodeActivity<int>klasy , ale jest deklarowane jako Activity tak, aby używane nieogólne przeciążenie Invoke , które zwraca słownik argumentów zamiast pojedynczej zwracanej wartości.

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("{0} / {1} = {2} Remainder {3}",
    dividend, divisor, outputs["Result"], outputs["Remainder"]);

Korzystanie z funkcji WorkflowApplication

WorkflowApplication Udostępnia bogaty zestaw funkcji zarządzania wystąpieniami przepływu pracy. WorkflowApplication działa jako bezpieczny wątek serwer proxy do rzeczywistego WorkflowInstance, który hermetyzuje środowisko uruchomieniowe i udostępnia metody tworzenia i ładowania wystąpień przepływu pracy, wstrzymywania i wznawiania, kończenie i powiadamianie o zdarzeniach cyklu życia. Aby uruchomić przepływ pracy przy użyciu polecenia WorkflowApplication WorkflowApplication, zasubskrybuj wszystkie żądane zdarzenia cyklu życia, uruchom przepływ pracy, a następnie zaczekaj na jego zakończenie. W tym przykładzie jest tworzona definicja przepływu pracy, która składa się z WriteLine działania, a WorkflowApplication obiekt jest tworzony przy użyciu określonej definicji przepływu pracy. Completed jest obsługiwane, więc host jest powiadamiany po zakończeniu przepływu pracy, przepływ pracy jest uruchamiany z wywołaniem metody Run, a następnie host czeka na ukończenie przepływu pracy. Po zakończeniu przepływu pracy parametr jest ustawiony, AutoResetEvent a aplikacja hosta może wznowić wykonywanie, jak pokazano w poniższym przykładzie.

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 Events

Oprócz Completedprogramu autorzy hostów mogą być powiadamiani, gdy przepływ pracy zostanie zwolniony (), przerwany (UnloadedAborted), stanie się bezczynny (Idle i PersistableIdle) lub wystąpi nieobsługiwany wyjątek (OnUnhandledException). Deweloperzy aplikacji przepływu pracy mogą obsługiwać te powiadomienia i podejmować odpowiednie działania, jak pokazano w poniższym przykładzie.

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

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

wfApp.Aborted = delegate (WorkflowApplicationAbortedEventArgs e)
{
    // Display the exception that caused the workflow
    // to abort.
    Console.WriteLine("Workflow {0} Aborted.", e.InstanceId);
    Console.WriteLine("Exception: {0}\n{1}",
        e.Reason.GetType().FullName,
        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 {0} Idle.", e.InstanceId);
};

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 {0} Unloaded.", e.InstanceId);
};

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

    Console.WriteLine("ExceptionSource: {0} - {1}",
        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;
};

Ustawianie argumentów wejściowych przepływu pracy

Dane mogą być przekazywane do przepływu pracy, ponieważ są one uruchamiane przy użyciu słownika parametrów, podobnie jak w przypadku używania polecenia WorkflowInvoker. Każdy element w słowniku jest mapowy na argument wejściowy określonego przepływu pracy. W tym przykładzie wywoływany jest przepływ pracy składający się z WriteLine działania, a jego Text argument jest określany przy użyciu słownika parametrów wejściowych.

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();

Pobieranie argumentów wyjściowych przepływu pracy

Po zakończeniu przepływu pracy wszystkie argumenty wyjściowe można pobrać w procedurze Completed obsługi przez uzyskanie dostępu do słownika WorkflowApplicationCompletedEventArgs.Outputs . Poniższy przykład hostuje przepływ pracy przy użyciu polecenia WorkflowApplication. Wystąpienie WorkflowApplication jest tworzone przy użyciu definicji przepływu pracy składającej się z jednego DiceRoll działania. Działanie DiceRoll ma dwa argumenty wyjściowe reprezentujące wyniki operacji rzutowania kostką. Po zakończeniu przepływu pracy dane wyjściowe są pobierane w procedurze Completed obsługi.

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 {0} Terminated.", e.InstanceId);
        Console.WriteLine("Exception: {0}\n{1}",
            e.TerminationException.GetType().FullName,
            e.TerminationException.Message);
    }
    else if (e.CompletionState == ActivityInstanceState.Canceled)
    {
        Console.WriteLine("Workflow {0} Canceled.", e.InstanceId);
    }
    else
    {
        Console.WriteLine("Workflow {0} Completed.", e.InstanceId);

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

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

Uwaga

WorkflowApplication i WorkflowInvoker weź słownik argumentów wejściowych i zwróć słownik argumentów out . Te parametry słownika, właściwości i wartości zwracane są typu IDictionary<string, object>. Rzeczywiste wystąpienie klasy słownika, która jest przekazywana, może być dowolną klasą implementowaną przez klasę IDictionary<string, object>. W tych przykładach Dictionary<string, object> jest używany. Aby uzyskać więcej informacji na temat słowników, zobacz IDictionary<TKey,TValue> i Dictionary<TKey,TValue>.

Przekazywanie danych do uruchomionego przepływu pracy przy użyciu zakładek

Zakładki to mechanizm, za pomocą którego działanie może pasywnie czekać na wznowienie i jest mechanizmem przekazywania danych do uruchomionego wystąpienia przepływu pracy. Jeśli działanie oczekuje na dane, może utworzyć Bookmark i zarejestrować metodę wywołania zwrotnego, która ma zostać wywołana po Bookmark wznowieniu działania, jak pokazano w poniższym przykładzie.

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);
    }

Po wykonaniu ReadLine działanie tworzy Bookmarkelement , rejestruje wywołanie zwrotne, a następnie czeka na Bookmark wznowienie działania. Po wznowieniu ReadLine działanie przypisuje dane przekazane z argumentem Bookmark .Result W tym przykładzie tworzony jest przepływ pracy, który używa ReadLine działania do zbierania nazwy użytkownika i wyświetlania go w oknie konsoli.

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: {0}", result);

Po wykonaniu ReadLine działania zostanie utworzone nazwane Bookmark UserName , a następnie zaczeka na wznowienie zakładki. Host zbiera żądane dane, a następnie wznawia działanie obiektu Bookmark. Przepływ pracy zostanie wznowione, wyświetli nazwę, a następnie zostanie ukończony.

Aplikacja hosta może sprawdzić przepływ pracy, aby określić, czy istnieją jakieś aktywne zakładki. Może to zrobić, wywołując GetBookmarks metodę WorkflowApplication wystąpienia lub sprawdzając WorkflowApplicationIdleEventArgs element w procedurze Idle obsługi.

Poniższy przykład kodu jest podobny do poprzedniego przykładu, z tą różnicą, że aktywne zakładki są wyliczane przed wznowieniu zakładki. Przepływ pracy jest uruchamiany, a po utworzeniu Bookmark i bezczynności GetBookmarks przepływ pracy jest wywoływany. Po zakończeniu przepływu pracy następujące dane wyjściowe zostaną wyświetlone w konsoli programu .

Jak się nazywasz?
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: {0} - OwnerDisplayName: {1}",
        info.BookmarkName, info.OwnerDisplayName);
}

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

Poniższy przykład kodu sprawdza WorkflowApplicationIdleEventArgs przekazany element do Idle programu obsługi WorkflowApplication wystąpienia. W tym przykładzie przepływ pracy w stanie bezczynności ma jedną Bookmark nazwę EnterGuess, należącą do działania o nazwie ReadInt. Ten przykład kodu jest oparty na artykule Instrukcje: uruchamianie przepływu pracy, który jest częścią samouczka wprowadzenie. Idle Jeśli program obsługi w tym kroku został zmodyfikowany tak, aby zawierał kod z tego przykładu, zostaną wyświetlone następujące dane wyjściowe.

BookmarkName: EnterGuess — OwnerDisplayName: ReadInt

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

    idleEvent.Set();
};

Podsumowanie

WorkflowInvoker Zapewnia lekki sposób wywoływania przepływów pracy i chociaż udostępnia metody przekazywania danych na początku przepływu pracy i wyodrębniania danych z ukończonego przepływu pracy, nie zapewnia bardziej złożonych scenariuszy, w których WorkflowApplication można używać.