Dela via


Använda WorkflowInvoker och WorkflowApplication

Windows Workflow Foundation (WF) innehåller flera metoder för att hantera arbetsflöden. WorkflowInvoker ger ett enkelt sätt att anropa ett arbetsflöde som om det vore ett metodanrop och kan endast användas för arbetsflöden som inte använder beständighet. WorkflowApplication tillhandahåller en mer omfattande modell för att köra arbetsflöden som innehåller meddelanden om livscykelhändelser, körningskontroll, återupptagande av bokmärken och beständighet. WorkflowServiceHost tillhandahåller stöd för meddelandeaktiviteter och används främst med arbetsflödestjänster. Det här avsnittet beskriver hur du hanterar arbetsflöden med WorkflowInvoker och WorkflowApplication. Mer information om hur du hanterar arbetsflöden med WorkflowServiceHostfinns i Arbetsflödestjänster och Översikt över arbetsflödestjänster.

Använda WorkflowInvoker

WorkflowInvoker tillhandahåller en modell för att köra ett arbetsflöde som om det vore ett metodanrop. Anropa ett arbetsflöde med genom WorkflowInvokeratt anropa Invoke metoden och skicka arbetsflödesdefinitionen för arbetsflödet för att anropa. I det här exemplet anropas en WriteLine aktivitet med hjälp av WorkflowInvoker.

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

WorkflowInvoker.Invoke(wf);

När ett arbetsflöde anropas med körs WorkflowInvokerarbetsflödet på den anropande tråden Invoke och metoden blockeras tills arbetsflödet har slutförts, inklusive inaktiv tid. Om du vill konfigurera ett tidsgränsintervall där arbetsflödet måste slutföras använder du en av de Invoke överlagringar som tar en TimeSpan parameter. I det här exemplet anropas ett arbetsflöde två gånger med två olika tidsgränsintervall. Det första arbetsflödet slutförs, men det andra gör det inte.

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

Kommentar

TimeoutException Genereras endast om tidsgränsintervallet förflutit och arbetsflödet blir inaktivt under körningen. Ett arbetsflöde som tar längre tid än det angivna tidsgränsintervallet för att slutföras slutförs om arbetsflödet inte blir inaktivt.

WorkflowInvoker innehåller också asynkrona versioner av metoden invoke. Mer information finns i InvokeAsync och BeginInvoke.

Ange indataargument för ett arbetsflöde

Data kan skickas till ett arbetsflöde med hjälp av en ordlista med indataparametrar, med argumentnamnet som mappas till indataargumenten i arbetsflödet. I det här exemplet anropas en WriteLine och värdet för argumentet Text anges med hjälp av ordlistan med indataparametrar.

Activity wf = new WriteLine();

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

WorkflowInvoker.Invoke(wf, inputs);

Hämtar utdataargument för ett arbetsflöde

Utdataparametrarna för ett arbetsflöde kan hämtas med hjälp av utdataordlistan som returneras från anropet till Invoke. I följande exempel anropas ett arbetsflöde som består av en enda Divide aktivitet som har två indataargument och två utdataargument. När arbetsflödet anropas skickas arguments ordlistan som innehåller värdena för varje indataargument, med namnet på argumentet. När anropet till Invoke returnerar returneras varje utdataargument i outputs ordlistan, som också är nyckelat efter argumentnamn.

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

Om arbetsflödet härleds från ActivityWithResult, till exempel CodeActivity<TResult> eller Activity<TResult>, och det finns utdataargument utöver det väldefinierade Result utdataargumentet, måste en icke-generisk överbelastning av Invoke användas för att hämta de ytterligare argumenten. För att göra detta måste arbetsflödesdefinitionen som skickas till Invoke vara av typen Activity. I det här exemplet Divide härleds aktiviteten från CodeActivity<int>, men deklareras som Activity så att en icke-allmän överlagring av Invoke används som returnerar en ordlista med argument i stället för ett enda returvärde.

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

Använda WorkflowApplication

WorkflowApplication innehåller en omfattande uppsättning funktioner för hantering av arbetsflödesinstanser. WorkflowApplication fungerar som en trådsäker proxy till den faktiska WorkflowInstance, som kapslar in körningen och tillhandahåller metoder för att skapa och läsa in arbetsflödesinstanser, pausa och återuppta, avsluta och meddela livscykelhändelser. Om du vill köra ett arbetsflöde med hjälp av WorkflowApplication skapar du WorkflowApplication, prenumererar på önskade livscykelhändelser, startar arbetsflödet och väntar sedan på att det ska slutföras. I det här exemplet skapas en arbetsflödesdefinition som består av en WriteLine aktivitet och en WorkflowApplication skapas med den angivna arbetsflödesdefinitionen. Completed hanteras så att värden meddelas när arbetsflödet har slutförts, arbetsflödet startas med ett anrop till Runoch sedan väntar värden på att arbetsflödet ska slutföras. När arbetsflödet är AutoResetEvent klart anges och värdprogrammet kan återuppta körningen, enligt följande exempel.

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

ArbetsflödeProgramlivscykelhändelser

Förutom Completedkan värdförfattare meddelas när ett arbetsflöde tas bort (Unloaded), avbryts (Aborted), blir inaktivt (Idle och PersistableIdle), eller om ett ohanterat undantag inträffar (OnUnhandledException). Utvecklare av arbetsflödesprogram kan hantera dessa meddelanden och vidta lämpliga åtgärder, enligt följande exempel.

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

Ange indataargument för ett arbetsflöde

Data kan skickas till ett arbetsflöde när de startas med hjälp av en ordlista med parametrar, på samma sätt som data skickas när du använder WorkflowInvoker. Varje objekt i ordlistan mappar till ett indataargument för det angivna arbetsflödet. I det här exemplet anropas ett arbetsflöde som består av en WriteLine aktivitet och dess Text argument anges med hjälp av ordlistan med indataparametrar.

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

Hämtar utdataargument för ett arbetsflöde

När ett arbetsflöde har slutförts kan alla utdataargument hämtas i Completed hanteraren genom att WorkflowApplicationCompletedEventArgs.Outputs komma åt ordlistan. I följande exempel finns ett arbetsflöde med .WorkflowApplication En WorkflowApplication instans skapas med hjälp av en arbetsflödesdefinition som består av en enda DiceRoll aktivitet. Aktiviteten DiceRoll har två utdataargument som representerar resultatet av tärningskaståtgärden. När arbetsflödet är klart hämtas utdata i Completed hanteraren.

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

Kommentar

WorkflowApplication och WorkflowInvoker ta en ordlista med indataargument och returnera en ordlista med out argument. Dessa ordlisteparametrar, egenskaper och returvärden är av typen IDictionary<string, object>. Den faktiska instansen av ordlisteklassen som skickas in kan vara vilken klass som helst som implementerar IDictionary<string, object>. I de här exemplen Dictionary<string, object> används. Mer information om ordlistor finns i IDictionary<TKey,TValue> och Dictionary<TKey,TValue>.

Skicka data till ett arbetsflöde som körs med hjälp av bokmärken

Bokmärken är den mekanism med vilken en aktivitet passivt kan vänta på att återupptas och är en mekanism för att skicka data till en instans av arbetsflödet som körs. Om en aktivitet väntar på data kan den skapa en Bookmark och registrera en återanropsmetod som ska anropas när den Bookmark återupptas, enligt följande exempel.

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

När den ReadLine körs skapar aktiviteten en Bookmark, registrerar ett återanrop och väntar sedan på Bookmark att återupptas. När den återupptas ReadLine tilldelar aktiviteten de data som skickades med Bookmark argumentet till .Result I det här exemplet skapas ett arbetsflöde som använder ReadLine aktiviteten för att samla in användarens namn och visa det i konsolfönstret.

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

När aktiviteten körs skapar den ett Bookmark namngivet ReadLineUserName och väntar sedan på att bokmärket ska återupptas. Värden samlar in önskade data och återupptar Bookmarksedan . Arbetsflödet återupptas, visar namnet och slutförs sedan.

Värdprogrammet kan granska arbetsflödet för att avgöra om det finns några aktiva bokmärken. Det kan du göra genom att anropa metoden för GetBookmarks en WorkflowApplication instans eller genom att WorkflowApplicationIdleEventArgs inspektera i Idle hanteraren.

Följande kodexempel liknar föregående exempel, förutom att de aktiva bokmärkena räknas upp innan bokmärket återupptas. Arbetsflödet startas och när Bookmark arbetsflödet har skapats och arbetsflödet är inaktivt GetBookmarks anropas det. När arbetsflödet har slutförts visas följande utdata i konsolen.

Vad heter du?
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());

I följande kodexempel inspekteras den WorkflowApplicationIdleEventArgs som skickats till hanteraren för Idle en WorkflowApplication instans. I det här exemplet har arbetsflödet som går inaktivt ett Bookmark med namnet EnterGuess, som ägs av en aktivitet med namnet ReadInt. Det här kodexemplet baseras på How to: Kör ett arbetsflöde, som är en del av självstudien Komma igång. Idle Om hanteraren i det steget ändras så att den innehåller koden från det här exemplet visas följande utdata.

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

Sammanfattning

WorkflowInvoker ger ett enkelt sätt att anropa arbetsflöden, och även om det innehåller metoder för att skicka in data i början av ett arbetsflöde och extrahera data från ett slutfört arbetsflöde, ger det inte mer komplexa scenarier där WorkflowApplication kan användas.