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 genom att använda WorkflowInvoker och kalla på Invoke-metoden samt skicka in arbetsflödesdefinitionen för det arbetsflöde som ska anropas. 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 WorkflowInvoker, körs arbetsflödet på den anropande tråden, och Invoke-metoden blockeras tills arbetsflödet har slutförts, inklusive inaktiv tid. Om du vill konfigurera en tidsgräns för arbetsflödets slutförande, använd en av de Invoke-överlagringar som har 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);
}

Anmärkning

TimeoutException genereras endast om tidsgränsintervallet löper ut och arbetsflödet är inaktivt under körning. 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 där varje nyckel motsvarar namnet på ett argument och matchar arbetsflödets indataargument. 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 dictionaryn, som innehåller värdena för varje indataargument, nycklad på argumentnamn. När anropet till Invoke återvänder, returneras varje utdataargument i outputs-uppslagslistan, vilka också är nycklade med argumentnamnet.

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($"{dividend} / {divisor} = {outputs["Result"]} Remainder {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 härrör Divide-aktiviteten från CodeActivity<int>, men deklareras som Activity så att en icke-generisk överlagring av Invoke används, vilken returnerar en dictionär 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($"{dividend} / {divisor} = {outputs["Result"]} Remainder {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 har slutförts, sätts AutoResetEvent och värdprogrammet kan återuppta körningen, vilket framgår av 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 {e.InstanceId} Terminated.");
        Console.WriteLine($"Exception: {e.TerminationException.GetType().FullName}\n{e.TerminationException.Message}");
    }
    else if (e.CompletionState == ActivityInstanceState.Canceled)
    {
        Console.WriteLine($"Workflow {e.InstanceId} Canceled.");
    }
    else
    {
        Console.WriteLine($"Workflow {e.InstanceId} Completed.");

        // Outputs can be retrieved from the Outputs dictionary,
        // keyed by argument name.
        // Console.WriteLine($"The winner is {e.Outputs["Winner"]}.");
    }
};

wfApp.Aborted = delegate (WorkflowApplicationAbortedEventArgs e)
{
    // Display the exception that caused the workflow
    // to abort.
    Console.WriteLine($"Workflow {e.InstanceId} Aborted.");
    Console.WriteLine($"Exception: {e.Reason.GetType().FullName}\n{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 {e.InstanceId} Idle.");
};

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 {e.InstanceId} Unloaded.");
};

wfApp.OnUnhandledException = delegate (WorkflowApplicationUnhandledExceptionEventArgs e)
{
    // Display the unhandled exception.
    Console.WriteLine($"OnUnhandledException in Workflow {e.InstanceId}\n{e.UnhandledException.Message}");

    Console.WriteLine($"ExceptionSource: {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 det startas med hjälp av en ordlista med parametrar, på samma sätt som data skickas in 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 av Completed-hanteraren genom att komma åt WorkflowApplicationCompletedEventArgs.Outputs-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 {e.InstanceId} Terminated.");
        Console.WriteLine($"Exception: {e.TerminationException.GetType().FullName}\n{e.TerminationException.Message}");
    }
    else if (e.CompletionState == ActivityInstanceState.Canceled)
    {
        Console.WriteLine($"Workflow {e.InstanceId} Canceled.");
    }
    else
    {
        Console.WriteLine($"Workflow {e.InstanceId} Completed.");

        // Outputs can be retrieved from the Outputs dictionary,
        // keyed by argument name.
        Console.WriteLine($"The two dice are {e.Outputs["D1"]} and {e.Outputs["D2"]}.");
    }
};

// Run the workflow.
wfApp.Run();

Anmärkning

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 ReadLine-aktiviteten körs, skapas en Bookmark, ett återanrop registreras, och sedan väntar man på att Bookmark ska återupptas. När aktiviteten återupptas tilldelar den de data som skickades med ReadLine till sitt Bookmark argument. 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: {result}");

När ReadLine-aktiviteten körs, skapas ett objekt av typen Bookmark med namnet UserName och väntar sedan på att bokmärket ska återupptas. Värden samlar in önskade data och sedan återupptar Bookmark. 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 göra detta genom att anropa GetBookmarks-metoden på en WorkflowApplication-instans, eller genom att inspektera WorkflowApplicationIdleEventArgs 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 har skapats och arbetsflödet blir inaktivt, anropas GetBookmarks. 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: {info.BookmarkName} - OwnerDisplayName: {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 en Idle-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å Hur man kör ett arbetsflöde, som är en del av Komma igång-självstudien. 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: {info.BookmarkName} - OwnerDisplayName: {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.