Sdílet prostřednictvím


Použití WorkflowInvoker a WorkflowApplication

Windows Workflow Foundation (WF) poskytuje několik metod hostování pracovních postupů. WorkflowInvoker poskytuje jednoduchý způsob, jak vyvolat pracovní postup, jako by se jednalo o volání metody a lze jej použít pouze pro pracovní postupy, které nepoužívají trvalost. WorkflowApplication poskytuje bohatší model pro provádění pracovních postupů, které zahrnují oznámení událostí životního cyklu, řízení provádění, obnovení záložky a trvalost. WorkflowServiceHost poskytuje podporu pro aktivity zasílání zpráv a primárně se používá se službami pracovních postupů. Toto téma vás seznámí s hostování pracovních postupů s WorkflowInvoker a WorkflowApplication. Další informace o hostování pracovních postupů pomocí WorkflowServiceHostnaleznete v tématu Služby pracovních postupů a Hostování Služeb pracovního postupu Přehled.

Použití WorkflowInvokeru

WorkflowInvoker poskytuje model pro provádění pracovního postupu, jako by se jednalo o volání metody. Chcete-li vyvolat pracovní postup pomocí WorkflowInvoker, zavolejte metodu Invoke a předejte definici pracovního postupu pracovního postupu k vyvolání. V tomto příkladu WriteLine je vyvolána aktivita pomocí WorkflowInvoker.

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

WorkflowInvoker.Invoke(wf);

Při vyvolání pracovního postupu pomocí WorkflowInvoker, pracovní postup se spustí na volajícím vlákně a Invoke metody blokuje, dokud nebude pracovní postup dokončen, včetně doby nečinnosti. Pokud chcete nakonfigurovat časový limit, ve kterém musí být pracovní postup dokončen, použijte jedno z Invoke přetížení, které přebírá TimeSpan parametr. V tomto příkladu se pracovní postup vyvolá dvakrát se dvěma různými intervaly časového limitu. První pracovní postup se dokončí, ale druhý ne.

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

Poznámka:

Je TimeoutException vyvolán pouze v případě, že časový limit uplynul a pracovní postup se během provádění stane nečinný. Pracovní postup, který trvá déle, než je zadaný interval časového limitu, se úspěšně dokončí, pokud pracovní postup nebude nečinný.

WorkflowInvoker poskytuje také asynchronní verze metody invoke. Další informace najdete v tématech InvokeAsync a BeginInvoke.

Nastavení vstupních argumentů pracovního postupu

Data lze předat do pracovního postupu pomocí slovníku vstupních parametrů, který je klíčován podle názvu argumentu, který se mapuje na vstupní argumenty pracovního postupu. V tomto příkladu WriteLine je vyvolána a hodnota argumentu Text je zadána pomocí slovníku vstupních parametrů.

Activity wf = new WriteLine();

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

WorkflowInvoker.Invoke(wf, inputs);

Načítání výstupních argumentů pracovního postupu

Výstupní parametry pracovního postupu lze získat pomocí slovníku výstupů, který je vrácen z volání do Invoke. Následující příklad vyvolá pracovní postup sestávající z jedné Divide aktivity, která má dva vstupní argumenty a dva výstupní argumenty. Při vyvolání pracovního postupu se předá arguments slovník obsahující hodnoty pro každý vstupní argument s klíčem podle názvu argumentu. Když se volání vrátí Invoke , vrátí se každý výstupní argument ve slovníku outputs , který je také klíč podle názvu 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"]);

Pokud pracovní postup pochází z ActivityWithResultargumentu výstupu ( například CodeActivity<TResult> nebo Activity<TResult>) a kromě dobře definovaného Result výstupního argumentu existují i výstupní argumenty, musí být použita ne generická přetížení Invoke , aby bylo možné načíst další argumenty. K tomu musí být definice pracovního postupu předána Invoke typu Activity. V tomto příkladu Divide aktivita je odvozena od CodeActivity<int>, ale je deklarována tak Activity , aby se použilo ne generické přetížení Invoke , které vrací slovník argumentů místo jedné návratové hodnoty.

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

Použití aplikace WorkflowApplication

WorkflowApplication poskytuje bohatou sadu funkcí pro správu instancí pracovního postupu. WorkflowApplication funguje jako proxy server bezpečný pro vlákno, WorkflowInstancekterý zapouzdřuje modul runtime, a poskytuje metody pro vytváření a načítání instancí pracovního postupu, pozastavení a obnovení, ukončení a oznámení událostí životního cyklu. Pokud chcete spustit pracovní postup pomocí WorkflowApplication vytvoření , přihlaste se k odběru WorkflowApplicationvšech požadovaných událostí životního cyklu, spusťte pracovní postup a počkejte, až se dokončí. V tomto příkladu se vytvoří definice pracovního postupu, která se skládá z WriteLine aktivity, a vytvoří se WorkflowApplication pomocí zadané definice pracovního postupu. Completed je zpracován, takže hostitel je upozorněn po dokončení pracovního postupu, pracovní postup se spustí s voláním Runa pak hostitel čeká na dokončení pracovního postupu. Po dokončení AutoResetEvent pracovního postupu se nastaví a hostitelská aplikace může pokračovat v provádění, jak je znázorněno v následujícím příkladu.

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

Události životního cyklu workflowApplication

Kromě Completedtoho mohou být autoři hostitelů upozorněni, když je pracovní postup uvolněn (Unloaded), přerušen (Aborted), stane se nečinným (Idle a PersistableIdle) nebo dojde k neošetřené výjimce (OnUnhandledException). Vývojáři aplikací pracovních postupů můžou tato oznámení zpracovat a provést odpovídající akci, jak je znázorněno v následujícím příkladu.

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

Nastavení vstupních argumentů pracovního postupu

Data mohou být předána do pracovního postupu při spuštění pomocí slovníku parametrů, podobně jako data se předávají při použití WorkflowInvoker. Každá položka ve slovníku se mapuje na vstupní argument zadaného pracovního postupu. V tomto příkladu je vyvolán pracovní postup, který se skládá z WriteLine aktivity, a jeho Text argument je určen pomocí slovníku vstupních parametrů.

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

Načítání výstupních argumentů pracovního postupu

Po dokončení pracovního postupu je možné v obslužné rutině Completed načíst všechny výstupní argumenty pomocí přístupu ke slovníku WorkflowApplicationCompletedEventArgs.Outputs . Následující příklad hostuje pracovní postup pomocí WorkflowApplication. Instance WorkflowApplication je vytvořena pomocí definice pracovního postupu sestávající z jedné DiceRoll aktivity. Aktivita DiceRoll má dva výstupní argumenty, které představují výsledky operace hodu kostky. Po dokončení pracovního postupu se výstupy načtou v obslužné rutině Completed .

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

Poznámka:

WorkflowApplication a WorkflowInvoker vezme slovník vstupních argumentů a vrátí slovník argumentů out . Tyto parametry slovníku, vlastnosti a návratové hodnoty jsou typu IDictionary<string, object>. Skutečná instance třídy slovníku, která je předána může být libovolná třída, která implementuje IDictionary<string, object>. V těchto příkladech Dictionary<string, object> se používá. Další informace o slovníkech najdete v tématu IDictionary<TKey,TValue> a Dictionary<TKey,TValue>.

Předávání dat do spuštěného pracovního postupu pomocí záložek

Záložky jsou mechanismus, pomocí kterého může aktivita pasivní čekání na obnovení a představují mechanismus pro předávání dat do spuštěné instance pracovního postupu. Pokud aktivita čeká na data, může vytvořit Bookmark a zaregistrovat metodu zpětného volání, která se má volat při Bookmark obnovení, jak je znázorněno v následujícím příkladu.

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

Při spuštění ReadLine aktivita vytvoří Bookmark, zaregistruje zpětné volání a pak čeká na Bookmark obnovení. Když se obnoví, ReadLine aktivita přiřadí data, která byla předána s argumentem BookmarkResult . V tomto příkladu se vytvoří pracovní postup, který pomocí ReadLine aktivity shromáždí jméno uživatele a zobrazí ho v okně konzoly.

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

Když se ReadLine aktivita spustí, vytvoří pojmenovanou BookmarkUserName a pak počká, až se záložka obnoví. Hostitel shromáždí požadovaná data a pak obnoví Bookmark. Pracovní postup se obnoví, zobrazí název a pak se dokončí.

Hostitelská aplikace může zkontrolovat pracovní postup a zjistit, jestli existují nějaké aktivní záložky. To může provést voláním GetBookmarks metody WorkflowApplication instance nebo kontrolou WorkflowApplicationIdleEventArgs v obslužné rutině Idle .

Následující příklad kódu je podobný předchozímu příkladu s tím rozdílem, že aktivní záložky jsou uvedené před pokračováním záložky. Pracovní postup se spustí a po Bookmark vytvoření pracovního postupu se zavolá nečinný GetBookmarks . Po dokončení pracovního postupu se v konzole zobrazí následující výstup.

Jak se jmenuješ?
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());

Následující příklad kódu zkontroluje WorkflowApplicationIdleEventArgs předanou obslužnou rutinu IdleWorkflowApplication instance. V tomto příkladu má pracovní postup nečinný jeden Bookmark s názvem EnterGuess, vlastněným aktivitou s názvem ReadInt. Tento příklad kódu vychází z postupu: Spuštění pracovního postupu, který je součástí kurzu Začínáme. Pokud je obslužná rutina Idle v tomto kroku upravena tak, aby obsahovala kód z tohoto příkladu, zobrazí se následující výstup.

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

Shrnutí

WorkflowInvoker poskytuje jednoduchý způsob, jak vyvolat pracovní postupy, a přestože poskytuje metody pro předávání dat na začátku pracovního postupu a extrahování dat z dokončeného pracovního postupu, neposkytuje složitější scénáře, kde WorkflowApplication lze použít.