Практическое руководство. Запуск рабочего процесса

Этот раздел продолжает учебник "Приступая к работе" для Windows Workflow Foundation и показывает, как создать узел рабочего процесса и выполнить рабочий процесс, описанный в предыдущем разделе How to: Create a Workflow .

Примечание.

Каждый раздел в учебнике «Приступая к работе» построен на основе предыдущих разделов. Для изучения этого раздела необходимо сначала пройти руководства How to: Create an Activity и How to: Create a Workflow.

Создание проекта узла рабочего процесса

  1. Откройте решение из предыдущей инструкции . Создание раздела действий с помощью Visual Studio 2012.

  2. Щелкните правой кнопкой мыши решение WF45GettingStartedTutorial в окне Обозреватель решений и выберите Добавить, Создать проект.

    Совет

    Если окно Обозреватель решений не отображается, в меню Вид выберите пункт Обозреватель решений .

  3. В узле Установленные выберите пункты Visual C#и Рабочий процесс (или Visual Basicи Рабочий процесс).

    Примечание.

    В зависимости от того, какой язык программирования задан как основной в Visual Studio, узел Visual C# или Visual Basic может находиться в разделе Другие языки узла Установленные .

    Убедитесь, что в раскрывающемся списке версий .NET Framework выбран пункт .NET Framework 4.5 . В списке Рабочий процесс выберите Консольное приложение рабочего процесса . Введите NumberGuessWorkflowHost в поле Имя и нажмите кнопку ОК. Будет создано начальное приложение рабочего процесса с базовой поддержкой размещения рабочего процесса. Этот базовый код размещения изменяется и используется для выполнения приложения рабочего процесса.

  4. Щелкните правой кнопкой мыши созданный проект NumberGuessWorkflowHost в обозревателе решений и выберите Добавить ссылку. Выберите Решение из списка Добавление ссылки , установите флажок рядом с NumberGuessWorkflowActivitiesи нажмите кнопку ОК.

  5. Щелкните правой кнопкой мыши Workflow1.xaml в окне Обозреватель решений и выберите Удалить. Нажмите кнопку ОК для подтверждения.

Изменение кода размещения рабочего процесса

  1. В Solution Explorer дважды щелкните Program.cs или Module1.vb для вывода кода.

    Совет

    Если окно Обозреватель решений не отображается, в меню Вид выберите пункт Обозреватель решений .

    Поскольку этот проект был создан с помощью шаблона Консольное приложение рабочего процесса , Program.cs или Module1.vb содержит следующий базовый код размещения рабочего процесса.

    ' Create and cache the workflow definition.
    Dim workflow1 As Activity = New Workflow1()
    WorkflowInvoker.Invoke(workflow1)
    
    // Create and cache the workflow definition.
    Activity workflow1 = new Workflow1();
    WorkflowInvoker.Invoke(workflow1);
    

    Этот код размещения использует WorkflowInvoker. WorkflowInvoker предоставляет простой способ вызова рабочего процесса аналогично вызову метода и может использоваться только для рабочих процессов, не использующих сохраняемость. WorkflowApplication предоставляет улучшенную модель выполнения рабочих процессов, которая включает уведомления о событиях жизненного цикла, управление выполнением, возобновление закладок и сохраняемость. В этом примере используются закладки, а для размещения рабочего процесса используется WorkflowApplication . Добавьте инструкцию using или Imports в начало файла Program.cs или Module1.vb после существующих инструкций using или Imports .

    Imports NumberGuessWorkflowActivities
    Imports System.Threading
    
    using NumberGuessWorkflowActivities;
    using System.Threading;
    

    Замените строки кода, использующие WorkflowInvoker , следующим базовым кодом размещения WorkflowApplication . Этот образец кода размещения показывает основные шаги по размещению и вызову рабочего процесса, но пока не обладает достаточной функциональностью для успешного выполнения рабочего процесса, описанного в данном разделе. В ходе следующих шагов этот базовый код модифицируется и добавляются дополнительные функции, пока приложение не будет полностью готово.

    Примечание.

    Замените Workflow1 в этих примерах на FlowchartNumberGuessWorkflow, SequentialNumberGuessWorkflowили StateMachineNumberGuessWorkflowв зависимости от рабочего процесса, который вы выполнили на предыдущем шаге How to: Create a Workflow . Если не заменить Workflow1 , то при попытке создать или запустить рабочий процесс будут выданы ошибки сборки.

    AutoResetEvent syncEvent = new AutoResetEvent(false);
    
    WorkflowApplication wfApp =
        new WorkflowApplication(new Workflow1());
    
    wfApp.Completed = delegate (WorkflowApplicationCompletedEventArgs e)
    {
        syncEvent.Set();
    };
    
    wfApp.Aborted = delegate (WorkflowApplicationAbortedEventArgs e)
    {
        Console.WriteLine(e.Reason);
        syncEvent.Set();
    };
    
    wfApp.OnUnhandledException = delegate (WorkflowApplicationUnhandledExceptionEventArgs e)
    {
        Console.WriteLine(e.UnhandledException.ToString());
        return UnhandledExceptionAction.Terminate;
    };
    
    wfApp.Run();
    
    syncEvent.WaitOne();
    
    Dim syncEvent As New AutoResetEvent(False)
    
    Dim wfApp As New WorkflowApplication(New Workflow1())
    
    wfApp.Completed = _
        Sub(e As WorkflowApplicationCompletedEventArgs)
            syncEvent.Set()
        End Sub
    
    wfApp.Aborted = _
        Sub(e As WorkflowApplicationAbortedEventArgs)
            Console.WriteLine(e.Reason)
            syncEvent.Set()
        End Sub
    
    wfApp.OnUnhandledException = _
        Function(e As WorkflowApplicationUnhandledExceptionEventArgs)
            Console.WriteLine(e.UnhandledException)
            Return UnhandledExceptionAction.Terminate
        End Function
    
    wfApp.Run()
    
    syncEvent.WaitOne()
    

    Этот код создает объект WorkflowApplication, подписывается на три события из жизненного цикла рабочего процесса, начинает рабочий процесс вызовом Run, а затем ждет завершения рабочего процесса. После завершения рабочего процесса устанавливается AutoResetEvent и ведущее приложение завершает работу.

Настройка входных аргументов рабочего процесса

  1. Добавьте следующую инструкцию в начало файла Program.cs или Module1.vb после существующих инструкций using или Imports .

    using System.Collections.Generic;
    using System.Threading;
    
    Imports System.Collections.Generic
    
  2. Замените строку кода, создающую новое WorkflowApplication , следующим кодом, который создает и передает словарь параметров рабочему процессу, когда процесс создается.

    Примечание.

    Замените Workflow1 в этих примерах на FlowchartNumberGuessWorkflow, SequentialNumberGuessWorkflowили StateMachineNumberGuessWorkflowв зависимости от рабочего процесса, который вы выполнили на предыдущем шаге How to: Create a Workflow . Если не заменить Workflow1 , то при попытке создать или запустить рабочий процесс будут выданы ошибки сборки.

    var inputs = new Dictionary<string, object>() { { "MaxNumber", 100 } };
    
    WorkflowApplication wfApp =
        new WorkflowApplication(new Workflow1(), inputs);
    
    Dim inputs As New Dictionary(Of String, Object)
    inputs.Add("MaxNumber", 100)
    
    Dim wfApp As New WorkflowApplication(New Workflow1(), inputs)
    

    В этом словаре содержится один элемент с ключом MaxNumber. Ключи в словаре входных данных соответствуют входным аргументам в корневом действии рабочего процесса. MaxNumber используется рабочим процессом для определения верхней границы случайного числа.

Извлечение выходных аргументов рабочего процесса

  1. Модифицируйте обработчик Completed , чтобы извлечь и отобразить число ходов, использованных рабочим процессом.

    wfApp.Completed = delegate (WorkflowApplicationCompletedEventArgs e)
    {
        int Turns = Convert.ToInt32(e.Outputs["Turns"]);
        Console.WriteLine("Congratulations, you guessed the number in {0} turns.", Turns);
    
        syncEvent.Set();
    };
    
    wfApp.Completed = _
        Sub(e As WorkflowApplicationCompletedEventArgs)
            Dim Turns As Integer = Convert.ToInt32(e.Outputs("Turns"))
            Console.WriteLine("Congratulations, you guessed the number in {0} turns.", Turns)
    
            syncEvent.Set()
        End Sub
    

Возобновление закладки

  1. Добавьте следующий код в начало метода Main сразу после существующего объявления AutoResetEvent .

    AutoResetEvent idleEvent = new AutoResetEvent(false);
    
    Dim idleEvent As New AutoResetEvent(False)
    
  2. Добавьте следующий обработчик Idle сразу после трех существующих обработчиков жизненного цикла процесса в Main.

    wfApp.Idle = delegate (WorkflowApplicationIdleEventArgs e)
    {
        idleEvent.Set();
    };
    
    wfApp.Idle = _
        Sub(e As WorkflowApplicationIdleEventArgs)
            idleEvent.Set()
        End Sub
    

    Каждый раз, когда рабочий процесс переходит в неактивный режим в ожидании следующего предположения, вызывается этот обработчик и устанавливается idleActionAutoResetEvent . Код на следующем этапе использует idleEvent и syncEvent , чтобы определить, ждет рабочий процесс следующего предположения или он завершился.

    Примечание.

    В этом примере ведущее приложение использует события автоматического сброса в обработчиках Completed и Idle , чтобы синхронизировать ведущее приложение с ходом выполнения рабочего процесса. Устанавливать блокировку и ждать неактивности рабочего процесса перед возобновлением закладки не обязательно, но в этом примере требуются события синхронизации, чтобы узел знал, завершен ли рабочий процесс, или он ждет дальнейшего ввода данных от пользователя с помощью Bookmark. Дополнительные сведения см. в разделе "Закладки".

  3. Удалите вызов WaitOne, замените его кодом для сборки входных данных от пользователя и возобновите Bookmark.

    Удалите следующую строку кода.

    syncEvent.WaitOne();
    
    syncEvent.WaitOne()
    

    Замените ее следующим примером.

    // Loop until the workflow completes.
    WaitHandle[] handles = new WaitHandle[] { syncEvent, idleEvent };
    while (WaitHandle.WaitAny(handles) != 0)
    {
        // Gather the user input and resume the bookmark.
        bool validEntry = false;
        while (!validEntry)
        {
            int Guess;
            if (!Int32.TryParse(Console.ReadLine(), out Guess))
            {
                Console.WriteLine("Please enter an integer.");
            }
            else
            {
                validEntry = true;
                wfApp.ResumeBookmark("EnterGuess", Guess);
            }
        }
    }
    
    ' Loop until the workflow completes.
    Dim waitHandles As WaitHandle() = New WaitHandle() {syncEvent, idleEvent}
    Do While WaitHandle.WaitAny(waitHandles) <> 0
        'Gather the user input and resume the bookmark.
        Dim validEntry As Boolean = False
        Do While validEntry = False
            Dim Guess As Integer
            If Int32.TryParse(Console.ReadLine(), Guess) = False Then
                Console.WriteLine("Please enter an integer.")
            Else
                validEntry = True
                wfApp.ResumeBookmark("EnterGuess", Guess)
            End If
        Loop
    Loop
    

Построение и запуск приложения

  1. Щелкните правой кнопкой мыши NumberGuessWorkflowHost в окне Обозреватель решений и выберите команду Установить как запускаемый проект.

  2. Нажмите клавиши CTRL+F5 для сборки и запуска приложения. Попробуйте угадать число с наименьшим числом попыток.

    Чтобы протестировать приложение с другим стилем рабочего процесса, замените Workflow1 в коде, который создает WorkflowApplication , на FlowchartNumberGuessWorkflow, SequentialNumberGuessWorkflowили StateMachineNumberGuessWorkflowв зависимости от того, какой стиль рабочего процесса нужен.

    var inputs = new Dictionary<string, object>() { { "MaxNumber", 100 } };
    
    WorkflowApplication wfApp =
        new WorkflowApplication(new Workflow1(), inputs);
    
    Dim inputs As New Dictionary(Of String, Object)
    inputs.Add("MaxNumber", 100)
    
    Dim wfApp As New WorkflowApplication(New Workflow1(), inputs)
    

    Инструкции по добавлению сохраняемости к приложению рабочего процесса см. в следующем разделе How to: Create and Run a Long Running Workflow.

Пример

Ниже приведен полный код для метода Main .

Примечание.

Замените Workflow1 в этих примерах на FlowchartNumberGuessWorkflow, SequentialNumberGuessWorkflowили StateMachineNumberGuessWorkflowв зависимости от рабочего процесса, который вы выполнили на предыдущем шаге How to: Create a Workflow . Если не заменить Workflow1 , то при попытке создать или запустить рабочий процесс будут выданы ошибки сборки.

static void Main(string[] args)
{
    AutoResetEvent syncEvent = new AutoResetEvent(false);
    AutoResetEvent idleEvent = new AutoResetEvent(false);

    var inputs = new Dictionary<string, object>() { { "MaxNumber", 100 } };

    WorkflowApplication wfApp =
        new WorkflowApplication(new Workflow1(), inputs);

    wfApp.Completed = delegate (WorkflowApplicationCompletedEventArgs e)
    {
        int Turns = Convert.ToInt32(e.Outputs["Turns"]);
        Console.WriteLine("Congratulations, you guessed the number in {0} turns.", Turns);

        syncEvent.Set();
    };

    wfApp.Aborted = delegate (WorkflowApplicationAbortedEventArgs e)
    {
        Console.WriteLine(e.Reason);
        syncEvent.Set();
    };

    wfApp.OnUnhandledException = delegate (WorkflowApplicationUnhandledExceptionEventArgs e)
    {
        Console.WriteLine(e.UnhandledException.ToString());
        return UnhandledExceptionAction.Terminate;
    };

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

    wfApp.Run();

    // Loop until the workflow completes.
    WaitHandle[] handles = new WaitHandle[] { syncEvent, idleEvent };
    while (WaitHandle.WaitAny(handles) != 0)
    {
        // Gather the user input and resume the bookmark.
        bool validEntry = false;
        while (!validEntry)
        {
            int Guess;
            if (!Int32.TryParse(Console.ReadLine(), out Guess))
            {
                Console.WriteLine("Please enter an integer.");
            }
            else
            {
                validEntry = true;
                wfApp.ResumeBookmark("EnterGuess", Guess);
            }
        }
    }
}
Sub Main()
    Dim syncEvent As New AutoResetEvent(False)
    Dim idleEvent As New AutoResetEvent(False)

    Dim inputs As New Dictionary(Of String, Object)
    inputs.Add("MaxNumber", 100)

    Dim wfApp As New WorkflowApplication(New Workflow1(), inputs)

    wfApp.Completed = _
        Sub(e As WorkflowApplicationCompletedEventArgs)
            Dim Turns As Integer = Convert.ToInt32(e.Outputs("Turns"))
            Console.WriteLine("Congratulations, you guessed the number in {0} turns.", Turns)

            syncEvent.Set()
        End Sub

    wfApp.Aborted = _
        Sub(e As WorkflowApplicationAbortedEventArgs)
            Console.WriteLine(e.Reason)
            syncEvent.Set()
        End Sub

    wfApp.OnUnhandledException = _
        Function(e As WorkflowApplicationUnhandledExceptionEventArgs)
            Console.WriteLine(e.UnhandledException)
            Return UnhandledExceptionAction.Terminate
        End Function

    wfApp.Idle = _
        Sub(e As WorkflowApplicationIdleEventArgs)
            idleEvent.Set()
        End Sub

    wfApp.Run()

    ' Loop until the workflow completes.
    Dim waitHandles As WaitHandle() = New WaitHandle() {syncEvent, idleEvent}
    Do While WaitHandle.WaitAny(waitHandles) <> 0
        'Gather the user input and resume the bookmark.
        Dim validEntry As Boolean = False
        Do While validEntry = False
            Dim Guess As Integer
            If Int32.TryParse(Console.ReadLine(), Guess) = False Then
                Console.WriteLine("Please enter an integer.")
            Else
                validEntry = True
                wfApp.ResumeBookmark("EnterGuess", Guess)
            End If
        Loop
    Loop
End Sub

См. также