Использование WorkflowInvoker и WorkflowApplicationUsing WorkflowInvoker and WorkflowApplication

Windows Workflow Foundation (WF) предоставляет несколько методов размещения рабочих процессов.Windows Workflow Foundation (WF) provides several methods of hosting workflows. WorkflowInvoker предоставляет простой способ вызова рабочего процесса аналогично вызову метода и может использоваться только для рабочих процессов, не использующих сохраняемость.WorkflowInvoker provides a simple way for invoking a workflow as if it were a method call and can be used only for workflows that do not use persistence. WorkflowApplication предоставляет улучшенную модель выполнения рабочих процессов, которая обеспечивает уведомления о событиях жизненного цикла, управление выполнением, возобновление закладок и сохраняемость.WorkflowApplication provides a richer model for executing workflows that includes notification of lifecycle events, execution control, bookmark resumption, and persistence. WorkflowServiceHost предоставляет поддержку для действий по обмену сообщениями и главным образом используется со службами Workflow Service.WorkflowServiceHost provides support for messaging activities and is primarily used with workflow services. В этом разделе вы познакомитесь с размещением рабочих процессов в WorkflowInvoker и WorkflowApplication.This topic introduces you to workflow hosting with WorkflowInvoker and WorkflowApplication. Дополнительные сведения о рабочих процессах размещения с помощью см WorkflowServiceHost . в разделе службы рабочих процессов и размещение общих сведений о службах рабочих процессов.For more information about hosting workflows with WorkflowServiceHost, see Workflow Services and Hosting Workflow Services Overview.

Использование WorkflowInvokerUsing WorkflowInvoker

WorkflowInvoker предоставляет модель для исполнения рабочего процесса аналогично вызову метода.WorkflowInvoker provides a model for executing a workflow as if it were a method call. Чтобы вызвать рабочий процесс при помощи WorkflowInvoker, вызовите метод Invoke и передайте определение вызываемого рабочего процесса.To invoke a workflow using WorkflowInvoker, call the Invoke method and pass in the workflow definition of the workflow to invoke. В данном примере действие WriteLine вызывается при помощи WorkflowInvoker.In this example, a WriteLine activity is invoked using WorkflowInvoker.

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

WorkflowInvoker.Invoke(wf);

Если рабочий процесс вызывается при помощи WorkflowInvoker, рабочий процесс исполняется в вызывающем потоке и метод Invoke блокируется до завершения рабочего процесса, включая период неактивности.When a workflow is invoked using WorkflowInvoker, the workflow executes on the calling thread and the Invoke method blocks until the workflow is complete, including any idle time. Чтобы задать интервал ожидания, в течение которого рабочий процесс должен завершиться, используйте одну из перегруженных версий метода Invoke, принимающую параметр TimeSpan.To configure a time-out interval in which the workflow must complete, use one of the Invoke overloads that takes a TimeSpan parameter. В данном примере рабочий процесс вызывается дважды с использованием двух разных интервалов ожидания.In this example, a workflow is invoked twice with two different time-out intervals. Первый рабочий процесс завершается, а второй не завершается.The first workflow complets, but the second does not.

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

Примечание

Исключение TimeoutException создается только в случае, если время ожидания истекло и рабочий процесс перешел в состояние бездействия во время выполнения.The TimeoutException is only thrown if the time-out interval elapses and the workflow becomes idle during execution. Рабочий процесс, не завершающийся в течение отведенного времени ожидания, завершается успешно, если не переходит в состояние простоя.A workflow that takes longer than the specified time-out interval to complete completes successfully if the workflow does not become idle.

WorkflowInvoker предоставляет также асинхронные версии метода вызова.WorkflowInvoker also provides asynchronous versions of the invoke method. Дополнительные сведения см. в разделах InvokeAsync и BeginInvoke.For more information, see InvokeAsync and BeginInvoke.

Настройка входных аргументов рабочего процессаSetting Input Arguments of a Workflow

Данные можно передать в рабочий процесс при помощи словаря входных параметров, ключом которого является имя аргумента, сопоставляемого с входными аргументами рабочего процесса.Data can be passed into a workflow using a dictionary of input parameters, keyed by argument name, that map to the input arguments of the workflow. В данном примере вызывается действие WriteLine, а значение его аргумента Text указывается при помощи словаря входных параметров.In this example, a WriteLine is invoked and the value for its Text argument is specified using the dictionary of input parameters.

Activity wf = new WriteLine();

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

WorkflowInvoker.Invoke(wf, inputs);

Получение выходных аргументов рабочего процессаRetrieving Output Arguments of a Workflow

Выходные параметры рабочего процесса можно получить при помощи словаря выходов, возвращаемого из вызова Invoke.The output parameters of a workflow can be obtained using the outputs dictionary that is returned from the call to Invoke. В следующем примере вызывается рабочий процесс, состоящий из одного действия Divide, которое имеет два входных аргумента и два выходных аргумента.The following example invokes a workflow consisting of a single Divide activity that has two input arguments and two output arguments. При вызове рабочего процесса передается словарь arguments, содержащий значения каждого входного аргумента с указанием имени аргумента.When the workflow is invoked, the arguments dictionary is passed which contains the values for each input argument, keyed by argument name. После завершения вызова Invoke в словаре выходных данных outputs возвращается каждый выходной аргумент с указанием имени.When the call to Invoke returns, each output argument is returned in the outputs dictionary, also keyed by argument name.

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"]);

Для рабочего процесса, производного от ActivityWithResult, такого как CodeActivity<TResult> или Activity<TResult>, если имеются выходные аргументы, помимо правильно определенного выходного аргумента Result, для получения дополнительных аргументов необходимо использовать неуниверсальную перегруженную версию Invoke.If the workflow derives from ActivityWithResult, such as CodeActivity<TResult> or Activity<TResult>, and there are output arguments in addition to the well-defined Result output argument, a non-generic overload of Invoke must be used in order to retrieve the additional arguments. Для этого определение рабочего процесса, переданное в Invoke, должно быть типа Activity.To do this, the workflow definition passed into Invoke must be of type Activity. В этом примере действие Divide является производным от CodeActivity<int>, но объявляется как Activity, поэтому используется неуниверсальная перегруженная версия Invoke, которая возвращает словарь аргументов вместо одиночного значения.In this example the Divide activity derives from CodeActivity<int>, but is declared as Activity so that a non-generic overload of Invoke is used which returns a dictionary of arguments instead of a single return value.

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"]);

Использование WorkflowApplicationUsing WorkflowApplication

WorkflowApplication предоставляет широкий набор возможностей для управления экземплярами рабочего процесса.WorkflowApplication provides a rich set of features for workflow instance management. WorkflowApplication действует как потокобезопасный прокси-сервер для настоящего WorkflowInstance, инкапсулирующего среду выполнения и предоставляющего методы для создания и загрузки экземпляров рабочего процесса, приостановки и возобновления, прекращения и уведомления о событиях жизненного цикла.WorkflowApplication acts as a thread safe proxy to the actual WorkflowInstance, which encapsulates the runtime, and provides methods for creating and loading workflow instances, pausing and resuming, terminating, and notification of lifecycle events. Чтобы запустить рабочий процесс при помощи WorkflowApplication, создайте WorkflowApplication, подпишитесь на любые желаемые события жизненного цикла, начните рабочий процесс и дождитесь его завершения.To run a workflow using WorkflowApplication you create the WorkflowApplication, subscribe to any desired lifecycle events, start the workflow, and then wait for it to finish. В данном примере создается определение рабочего процесса, состоящее из действия WriteLine, и WorkflowApplication с помощью указанного определения рабочего процесса.In this example, a workflow definition that consists of a WriteLine activity is created and a WorkflowApplication is created using the specified workflow definition. Completed обрабатывается, чтобы узел получил уведомление о завершении рабочего процесса. Рабочий процесс начался вызовом Run, и узел ожидает завершения рабочего процесса.Completed is handled so the host is notified when the workflow completes, the workflow is started with a call to Run, and then the host waits for the workflow to complete. После завершения рабочего процесса задается AutoResetEvent и ведущее приложение может возобновить выполнение, как показано в следующем примере.When the workflow completes, the AutoResetEvent is set and the host application can resume execution, as shown in the following example.

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

События жизненного цикла WorkflowApplicationWorkflowApplication Lifecycle Events

В дополнение к Completed авторы узла могут получать уведомление, когда рабочий процесс выгружается (Unloaded), прекращается (Aborted), переходит в состояние бездействия (Idle и PersistableIdle) или когда происходит необработанное исключение (OnUnhandledException).In addition to Completed, host authors can be notified when a workflow is unloaded (Unloaded), aborted (Aborted), becomes idle (Idle and PersistableIdle), or an unhandled exception occurs (OnUnhandledException). Разработчики приложения рабочего процесса могут обработать эти уведомления и предпринять надлежащие меры, как показано в следующем примере.Workflow application developers can handle these notifications and take appropriate action, as shown in the following example.

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

Настройка входных аргументов рабочего процессаSetting Input Arguments of a Workflow

Данные могут передаваться в рабочий процесс в его начале при помощи словаря параметров, подобно тому, как данные передаются при помощи WorkflowInvoker.Data can be passed into a workflow as it is started using a dictionary of parameters, similar to the way data is passed in when using WorkflowInvoker. Каждый элемент в словаре сопоставляется с входным аргументом указанного рабочего процесса.Each item in the dictionary maps to an input argument of the specified workflow. В данном примере вызывается рабочий процесс, состоящий из действия WriteLine, и его аргумент Text указывается при помощи словаря входных параметров.In this example, a workflow that consists of a WriteLine activity is invoked and its Text argument is specified using the dictionary of input parameters.

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

Получение выходных аргументов рабочего процессаRetrieving Output Arguments of a Workflow

После завершения рабочего процесса любой из выходных аргументов можно извлечь в обработчик Completed, обратившись к словарю WorkflowApplicationCompletedEventArgs.Outputs.When a workflow completes, any output arguments can be retrieved in the Completed handler by accessing the WorkflowApplicationCompletedEventArgs.Outputs dictionary. В следующем примере рабочий процесс размещается с помощью WorkflowApplication.The following example hosts a workflow using WorkflowApplication. WorkflowApplicationЭкземпляр создается с использованием определения рабочего процесса, состоящего из одного DiceRoll действия.A WorkflowApplication instance is constructed using a workflow definition consisting of a single DiceRoll activity. Действие DiceRoll имеет два выходных аргумента, представляющих результаты броска игральных костей.The DiceRoll activity has two output arguments that represent the results of the dice roll operation. После завершения рабочего процесса выходные параметры возвращаются из обработчика Completed.When the workflow completes, the outputs are retrieved in the Completed handler.

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

Примечание

WorkflowApplication и WorkflowInvoker получают словарь входных аргументов и возвращают словарь аргументов out.WorkflowApplication and WorkflowInvoker take a dictionary of input arguments and return a dictionary of out arguments. Эти параметры словаря, свойства и возвращаемые значения имеют тип IDictionary<string, object>.These dictionary parameters, properties, and return values are of type IDictionary<string, object>. Фактически передаваемым экземпляром класса словаря может быть любой класс, который реализует IDictionary<string, object>.The actual instance of the dictionary class that is passed in can be any class that implements IDictionary<string, object>. В этих примерах используется Dictionary<string, object>.In these examples, Dictionary<string, object> is used. Дополнительные сведения о словарях см. в статьях IDictionary<TKey,TValue> и Dictionary<TKey,TValue> .For more information about dictionaries, see IDictionary<TKey,TValue> and Dictionary<TKey,TValue>.

Передача данных в запущенный рабочий процесс при помощи закладокPassing Data into a Running Workflow Using Bookmarks

Закладки - это механизм, при помощи которого действие может пассивно ждать возобновления; с их помощью можно передавать данные в запущенный экземпляр рабочего процесса.Bookmarks are the mechanism by which an activity can passively wait to be resumed and are a mechanism for passing data into a running workflow instance. Если действие ждет данные, оно может создать Bookmark и зарегистрировать метод обратного вызова, который будет вызываться, когда возобновляется Bookmark, как показано в следующем примере.If an activity is waiting for data, it can create a Bookmark and register a callback method to be called when the Bookmark is resumed, as shown in the following example.

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

При выполнении действие ReadLine создает Bookmark, регистрирует обратный вызов и ждет возобновления чтения с закладки Bookmark.When executed, the ReadLine activity creates a Bookmark, registers a callback, and then waits for the Bookmark to be resumed. После возобновления чтения с закладки действие ReadLine присваивает данные, переданные с закладкой Bookmark, своему аргументу Result.When it is resumed, the ReadLine activity assigns the data that was passed with the Bookmark to its Result argument. В следующем примере создается рабочий процесс, использующий действие ReadLine для получения имени пользователя и его отображения в окне консоли.In this example, a workflow is created that uses the ReadLine activity to gather the user’s name and display it to the console window.

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

При выполнении действие ReadLine создает закладку Bookmark с именем UserName и ждет возобновления чтения с этой закладки.When the ReadLine activity is executed, it creates a Bookmark named UserName and then waits for the bookmark to be resumed. Узел собирает необходимые данные и возобновляет чтение с закладки Bookmark.The host collects the desired data and then resumes the Bookmark. Рабочий процесс возобновляется, отображает имя и затем завершается.The workflow resumes, displays the name, and then completes.

Ведущее приложение может проверять рабочий процесс на наличие активных закладок.The host application can inspect the workflow to determine if there are any active bookmarks. Это осуществляется вызовом метода GetBookmarks экземпляра WorkflowApplication или проверкой WorkflowApplicationIdleEventArgs в обработчике Idle.It can do this by calling the GetBookmarks method of a WorkflowApplication instance, or by inspecting the WorkflowApplicationIdleEventArgs in the Idle handler.

Следующий пример кода подобен предыдущему примеру, за исключением того, что активные закладки перечисляются до возобновления закладки.The following code example is like the previous example except that the active bookmarks are enumerated before the bookmark is resumed. Рабочий процесс запускается, и, когда создается Bookmark, а рабочий процесс переходит в состояние бездействия, вызывается метод GetBookmarks.The workflow is started, and once the Bookmark is created and the workflow goes idle, GetBookmarks is called. После завершения рабочего процесса на консоль выводятся следующие данные.When the workflow is completed, the following output is displayed to the console.

Как вас зовут?What is your name?
BookmarkName: username-овнердисплайнаме: ReadLine Стив Привет, ВикторBookmarkName: UserName - OwnerDisplayName: ReadLine Steve Hello, 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());

В следующем примере кода проверяются аргументы WorkflowApplicationIdleEventArgs, переданные в обработчик Idle экземпляра WorkflowApplication.The following code example inspects the WorkflowApplicationIdleEventArgs passed into the Idle handler of a WorkflowApplication instance. В этом примере рабочий процесс, переходящий в состояние бездействия, содержит одну закладку Bookmark с именем EnterGuess, которая принадлежит действию с именем ReadInt.In this example the workflow going idle has one Bookmark with a name of EnterGuess, owned by an activity named ReadInt. Этот пример кода основан на том, как запустить рабочий процесс, который является частью учебника по начало работы.This code example is based off of How to: Run a Workflow, which is part of the Getting Started Tutorial. Если на данном этапе изменить обработчик Idle и включить код из этого примера, то будут выведены следующие данные.If the Idle handler in that step is modified to contain the code from this example, the following output is displayed.

BookmarkName: EnterGuess — OwnerDisplayName: ReadIntBookmarkName: 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();
};

СводкаSummary

WorkflowInvoker предоставляет упрощенный способ вызова рабочих процессов, и, хотя он предоставляет методы для передачи данных в начале рабочего процесса и извлечения данных из завершенного процесса, он не предусмотрен для более сложных сценариев, в которых можно использовать WorkflowApplication.WorkflowInvoker provides a lightweight way to invoke workflows, and although it provides methods for passing data in at the start of a workflow and extracting data from a completed workflow, it does not provide for more complex scenarios which is where WorkflowApplication can be used.