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

Этот раздел является продолжением руководства "Начало работы" для 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, в зависимости от того, какой рабочий процесс вы завершили на предыдущем шаге Как создать рабочий процесс. Если не заменить Workflow1, то при попытке собрать или запустить рабочий процесс возникнут ошибки сборки.

    AutoResetEvent syncEvent = new AutoResetEvent(false);
    
    WorkflowApplication wfApp =
        new WorkflowApplication(_wf);
    
    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 .

  2. Замените строку кода, создающую новое WorkflowApplication, следующим кодом, который создает и передает словарь параметров рабочему процессу при его создании.

    Примечание.

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

    var inputs = new Dictionary<string, object>() { { "MaxNumber", 100 } };
    
    WorkflowApplication wfApp = new(_wf, 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 , чтобы извлечь и отобразить число ходов, использованных рабочим процессом.

    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.

        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)
        {
            if (!Int32.TryParse(Console.ReadLine(), out int 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(_wf, 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(_wf, inputs)
    {

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

            syncEvent.Set();
        },

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

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

        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)
        {
            if (!Int32.TryParse(Console.ReadLine(), out int 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

См. также