A WorkflowInvoker és a WorkflowApplication használata

A Windows Workflow Foundation (WF) számos módszert kínál a munkafolyamatok üzemeltetésére. WorkflowInvoker Egy munkafolyamat meghívásának egyszerű módja, mintha metódushívás lenne, és csak olyan munkafolyamatokhoz használható, amelyek nem használnak adatmegőrzést. WorkflowApplication Gazdagabb modellt biztosít a munkafolyamatok végrehajtásához, beleértve az életciklus-események értesítését, a végrehajtás-vezérlést, a könyvjelzők újraindítását és az adatmegőrzést. WorkflowServiceHost támogatja az üzenetkezelési tevékenységeket, és elsősorban munkafolyamat-szolgáltatásokhoz használják. Ez a témakör bemutatja a munkafolyamat-üzemeltetést a WorkflowInvoker és a WorkflowApplication segítségével. A munkafolyamatok üzemeltetésével WorkflowServiceHostkapcsolatos további információkért lásd: Workflow Services and Hosting Workflow Services – Áttekintés.

A WorkflowInvoker használata

WorkflowInvoker olyan modellt biztosít a munkafolyamatok végrehajtásához, mintha metódushívás lenne. A munkafolyamat WorkflowInvokermeghívásához hívja meg a Invoke metódust, és adja meg a meghívandó munkafolyamat munkafolyamat-definícióját. Ebben a példában egy WriteLine tevékenységet hívnak meg az WorkflowInvoker használatával.

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

WorkflowInvoker.Invoke(wf);

Amikor egy munkafolyamatot meghív a rendszer WorkflowInvoker, a munkafolyamat a hívó szálon fut, és a Invoke metódus blokkolja a szálat a munkafolyamat befejezéséig, beleértve a tétlen időt is. Az időkorlát intervallumának beállításához, amelyen belül a munkafolyamatnak be kell fejeződnie, használja a Invoke paramétert tartalmazó TimeSpan túlterhelési lehetőségek egyikét. Ebben a példában a munkafolyamat két különböző időtúllépési időközzel kétszer lesz meghívva. Az első munkafolyamat befejeződik, de a második nem.

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

Megjegyzés

A TimeoutException csak akkor vet ki hibát, ha az időkorlát letelik, és a munkafolyamat végrehajtás közben tétlenné válik. A megadott időtúllépési időköznél hosszabb ideig tartó munkafolyamat sikeresen befejeződik, ha a munkafolyamat nem válik tétlenné.

WorkflowInvoker A meghívási metódus aszinkron verzióit is biztosítja. További információ: InvokeAsync és BeginInvoke.

Munkafolyamat bemeneti argumentumainak beállítása

Az adatok a munkafolyamatba továbbíthatók olyan bemeneti paraméterek szótárával, amelyek argumentumnév alapján lesznek megadva, és amelyek a munkafolyamat bemeneti argumentumaihoz lesznek megfeleltetve. Ebben a példában egy WriteLine meghívás történik, és az argumentum értéke Text a bemeneti paraméterek szótárával van megadva.

Activity wf = new WriteLine();

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

WorkflowInvoker.Invoke(wf, inputs);

Munkafolyamat kimeneti argumentumainak beolvasása

A munkafolyamat kimeneti paraméterei lekérhetők a Invoke hívásból visszakapott kimeneti szótár használatával. Az alábbi példa egy olyan munkafolyamatot hív meg, amely egy olyan Divide tevékenységből áll, amely két bemeneti argumentumot és két kimeneti argumentumot tartalmaz. A munkafolyamat meghívásakor a rendszer átadja a arguments szótárt, amely tartalmazza az egyes bemeneti argumentumok értékeit, argumentumnév alapján. A Invoke függvény hívása után minden kimeneti argumentumot a outputs szótár ad vissza, az argumentum neve alapján kulcsolva.

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

Ha a munkafolyamat a ActivityWithResult-ból származik, mint például a CodeActivity<TResult> vagy a Activity<TResult>, és a jól definiált Result kimeneti argumentum mellett további kimeneti argumentumok is vannak, akkor a további argumentumok lekéréséhez a nem általános Invoke túlterhelést kell használni. Ehhez a munkafolyamat-definíciónak Invoke típusnak Activitykell lennie. Ebben a példában a Divide tevékenység a CodeActivity<int>-ból/ből származik, de úgy van deklarálva, mint Activity, hogy a rendszer egy nem általanos túlterhelést használjon Invoke, amely argumentumok szótárát adja vissza egyetlen visszatérési érték helyett.

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

A WorkflowApplication használata

WorkflowApplication számos funkciót biztosít a munkafolyamat-példányok kezeléséhez. WorkflowApplication szálbiztos proxyként működik a tényleges WorkflowInstance számára, amely a futtatókörnyezetet beágyazza, és metódusokat biztosít a munkafolyamat-példányok létrehozásához és betöltéséhez, szüneteltetéséhez és folytatásához, leállításához, valamint az életciklus-események értesítéséhez. A munkafolyamat futtatásához a WorkflowApplication használatával először hozza létre a WorkflowApplication-t, iratkozzon fel a kívánt életciklus-eseményekre, indítsa el a munkafolyamatot, majd várja meg, amíg befejeződik. Ebben a példában egy tevékenységből álló WriteLine munkafolyamat-definíció jön létre, és egy WorkflowApplication a megadott munkafolyamat-definícióval jön létre. A Completed kezelése úgy történik, hogy a gazdagép értesítést kap, amikor a munkafolyamat befejeződik. A munkafolyamat egy Run hívással indul el, majd a gazdagép megvárja annak befejezését. Amikor a munkafolyamat befejeződik, be van állítva a AutoResetEvent beállítás, és a gazdaalkalmazás folytathatja a végrehajtást, ahogy az alábbi példában is látható.

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

A WorkflowApplication életciklus eseményei

Emellett a gazdagép-szerzők értesítést kaphatnak, amikor egy munkafolyamat betöltésének megszüntetése vagy megszakítása történik (Completed, Unloaded), tétlenné válik (Aborted és Idle), vagy ha nem kezelt kivétel fordul elő (PersistableIdle). A munkafolyamat-alkalmazás fejlesztői kezelhetik ezeket az értesítéseket, és elvégezhetik a megfelelő műveletet az alábbi példában látható módon.

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

Munkafolyamat bemeneti argumentumainak beállítása

Az adatok a munkafolyamat indításakor adhatók át egy paraméterszótár használatával, hasonlóan ahhoz, ahogyan az adatok továbbítása történik a WorkflowInvoker használata során. A szótár minden eleme megfelel a megadott munkafolyamat bemeneti argumentumának. Ebben a példában egy tevékenységből álló WriteLine munkafolyamatot hív meg a rendszer, amelynek argumentuma Text a bemeneti paraméterek szótárával van megadva.

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

Munkafolyamat kimeneti argumentumainak beolvasása

Amikor egy munkafolyamat befejeződik, a kimeneti argumentumok lekérdezhetők a Completed kezelőben a WorkflowApplicationCompletedEventArgs.Outputs szótár elérésével. Az alábbi példa egy munkafolyamatot üzemeltet a következővel WorkflowApplication: . A WorkflowApplication példányok létrehozása egy munkafolyamat-definícióval történik, amely egyetlen DiceRoll tevékenységből áll. A DiceRoll tevékenységnek két kimeneti argumentuma van, amelyek a kockatekercselési művelet eredményeit jelölik. Amikor a munkafolyamat befejeződik, a kimenetek a Completed kezelőben kerülnek lekérésre.

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

Megjegyzés

WorkflowApplication és WorkflowInvoker elfogad egy bemeneti argumentumok szótárát, és visszaad egy out argumentumok szótárát. Ezeknek a szótárparamétereknek, tulajdonságoknak és visszatérési értékeknek a típusa IDictionary<string, object>. Az átadott szótárosztály tényleges példánya bármely olyan osztály lehet, amely megvalósítja IDictionary<string, object>a elemet. Ezekben a példákban a Dictionary<string, object> van használatban. További információ a szótárakról a IDictionary<TKey,TValue> és Dictionary<TKey,TValue> résznél található.

Adatok továbbítása futó munkafolyamatba könyvjelzőkkel

A könyvjelzők az a mechanizmus, amellyel egy tevékenység passzívan várhat a folytatásra, és az adatok futó munkafolyamat-példányba való továbbításának mechanizmusa. Ha egy tevékenység adatokra vár, létrehozhat és regisztrálhat egy Bookmark visszahívási metódust, amelyet a Bookmark rendszer meghív a folytatáskor, ahogy az az alábbi példában is látható.

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

A végrehajtáskor a ReadLine tevékenység létrehoz egy Bookmark, regisztrál egy visszahívást, majd megvárja a Bookmark folytatását. A folytatáskor a ReadLine tevékenység hozzárendeli azokat az adatokat, amelyeket az Bookmark által továbbított Result argumentumnak. Ebben a példában létrejön egy munkafolyamat, amely a ReadLine tevékenység használatával gyűjti össze a felhasználó nevét, és megjeleníti azt a konzolablakban.

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

ReadLine A tevékenység végrehajtásakor létrehoz egy `Bookmark` nevű `UserName`-t, majd megvárja a könyvjelző folytatását. A kiszolgáló összegyűjti a kívánt adatokat, majd folytatja a Bookmark. A munkafolyamat folytatódik, megjeleníti a nevet, majd befejeződik.

A gazdaalkalmazás megvizsgálhatja a munkafolyamatot annak megállapításához, hogy vannak-e aktív könyvjelzők. Ezt a GetBookmarks példány WorkflowApplication metódusának meghívásával vagy a WorkflowApplicationIdleEventArgs kezelőben a Idle vizsgálatával teheti meg.

Az alábbi példakód az előző példához hasonló, azzal a kivételsel, hogy az aktív könyvjelzők felsorolása a könyvjelző folytatása előtt történik. A munkafolyamat elindul, és amikor a Bookmark létrejön, a munkafolyamat tétlen állapotba kerül, majd meghívódik a GetBookmarks. A munkafolyamat befejezésekor a következő kimenet jelenik meg a konzolon.

Hogy hívnak?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());

A következő példakód megvizsgálja a WorkflowApplicationIdleEventArgs-t, amely a Idle példány WorkflowApplication kezelőjéhez lett továbbítva. Ebben a példában a tétlen munkafolyamatnak van egy Bookmark, amelynek a neve EnterGuess, és amely egy ReadInt nevű tevékenység tulajdonában van. Ez a példakód a How to: Run a Workflow (Munkafolyamat futtatása) című témakörön alapul, amely az Első lépések oktatóanyag része. Ha a Idle lépés kezelőjét úgy módosítják, hogy az tartalmazza a példában szereplő kódot, a következő kimenet jelenik meg.

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

Összefoglalás

WorkflowInvoker Egyszerű módot kínál a munkafolyamatok meghívására, és bár metódusokat biztosít az adatok munkafolyamat elején történő továbbításához és az adatok egy befejezett munkafolyamatból való kinyeréséhez, nem nyújt összetettebb forgatókönyveket, amelyek használhatók WorkflowApplication .