Partager via


Utilisation de WorkflowInvoker et workflowApplication

Windows Workflow Foundation (WF) fournit plusieurs méthodes d’hébergement de flux de travail. WorkflowInvoker fournit un moyen simple d’appeler un flux de travail comme s’il s’agissait d’un appel de méthode et ne peut être utilisé que pour les flux de travail qui n’utilisent pas la persistance. WorkflowApplication fournit un modèle plus riche pour l’exécution de flux de travail qui inclut la notification des événements de cycle de vie, le contrôle d’exécution, la reprise des signets et la persistance. WorkflowServiceHost assure la prise en charge des activités de messagerie et est principalement utilisée avec les services de flux de travail. Cette rubrique vous présente l’hébergement de flux de travail avec WorkflowInvoker et WorkflowApplication. Pour plus d’informations sur l’hébergement de flux de travail avec WorkflowServiceHost, consultez Workflow Services et Hosting Workflow Services Overview.

Utilisation de WorkflowInvoker

WorkflowInvoker fournit un modèle pour l’exécution d’un flux de travail comme s’il s’agissait d’un appel de méthode. Pour invoquer un flux de travail à l'aide de WorkflowInvoker, appelez la méthode Invoke et transmettez la définition du flux de travail à invoquer. Dans cet exemple, une WriteLine activité est appelée à l’aide de WorkflowInvoker.

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

WorkflowInvoker.Invoke(wf);

Lorsqu’un flux de travail est appelé à l’aide WorkflowInvoker, le flux de travail s’exécute sur le thread appelant et la Invoke méthode se bloque jusqu’à ce que le flux de travail soit terminé, y compris toute heure d’inactivité. Pour configurer un intervalle de délai d'attente au cours duquel le workflow doit être exécuté, utilisez l'une des surcharges de Invoke acceptant un paramètre TimeSpan. Dans cet exemple, un flux de travail est appelé deux fois avec deux intervalles de délai d’attente différents. Le premier flux de travail se termine, mais le second ne le fait pas.

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

Remarque

L'exception TimeoutException n'est déclenchée que si le délai d'attente s'écoule et que le flux de travail devient inactif pendant l'exécution. Un flux de travail qui prend plus de temps que l’intervalle de délai d’attente spécifié pour se terminer correctement si le flux de travail ne devient pas inactif.

WorkflowInvoker fournit également des versions asynchrones de la méthode Invoke. Pour plus d’informations, consultez InvokeAsync et BeginInvoke.

Définition d’arguments d’entrée d’un flux de travail

Les données peuvent être transmises à un flux de travail à l’aide d’un dictionnaire de paramètres d’entrée, associées au nom d’argument, qui correspondent aux arguments d'entrée du flux de travail. Dans cet exemple, un WriteLine est appelé et la valeur de son Text argument est spécifiée à l’aide du dictionnaire des paramètres d’entrée.

Activity wf = new WriteLine();

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

WorkflowInvoker.Invoke(wf, inputs);

Récupération d’arguments de sortie d’un flux de travail

Les paramètres de sortie d’un flux de travail peuvent être obtenus à l’aide du dictionnaire de sorties retourné par l’appel à Invoke. L’exemple suivant appelle un flux de travail constitué d’une activité unique Divide qui a deux arguments d’entrée et deux arguments de sortie. Lorsque le workflow est appelé, le dictionnaire d'arguments (arguments) est passé ; il contient les valeurs de chaque argument d'entrée, indexées par nom d'argument. Lorsque l’appel à Invoke retourne, chaque argument de sortie est retourné dans le dictionnaire outputs, également indexé par nom d’argument.

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

Si le flux de travail dérive de ActivityWithResult, comme CodeActivity<TResult> ou Activity<TResult>, et qu’il y a des arguments de sortie en plus de l’argument de sortie bien défini Result, une surcharge non générique de Invoke doit être utilisée pour récupérer les arguments supplémentaires. Pour ce faire, la définition de flux de travail passée Invoke doit être de type Activity. Dans cet exemple, l’activité Divide dérive de CodeActivity<int>, mais est déclarée en tant que Activity afin qu’une surcharge non générique de Invoke soit utilisée, retournant un dictionnaire d’arguments au lieu d’une valeur de retour unique.

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

Utilisation de WorkflowApplication

WorkflowApplication fournit un ensemble complet de fonctionnalités pour la gestion des instances de flux de travail. WorkflowApplication agit comme un proxy sécurisé de thread vers le runtime réel WorkflowInstance, qui encapsule le runtime et fournit des méthodes pour créer et charger des instances de flux de travail, suspendre et reprendre, mettre fin et notification des événements de cycle de vie. Pour exécuter un flux de travail à l'aide de WorkflowApplication, vous créez le WorkflowApplication, vous vous abonnez à tous les événements de cycle de vie souhaités, démarrez le flux de travail, puis attendez qu'il se termine. Dans cet exemple, une définition de flux de travail constituée d’une WriteLine activité est créée, puis une WorkflowApplication est créée à l’aide de la définition de flux de travail spécifiée. Completed est géré afin que l’hôte soit averti lorsque le flux de travail est terminé, le flux de travail est démarré avec un appel, Runpuis l’hôte attend que le flux de travail se termine. Une fois que AutoResetEvent est défini et que le flux de travail est terminé, l'application hôte peut reprendre l'exécution, comme illustré dans l'exemple suivant.

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

Événements de cycle de vie WorkflowApplication

En plus de Completed, les auteurs hôtes peuvent être avertis lorsqu’un flux de travail est déchargé (Unloaded), abandonné (Aborted), devient inactif (Idle et PersistableIdle) ou qu’une exception non gérée se produit (OnUnhandledException). Les développeurs d’applications de flux de travail peuvent gérer ces notifications et prendre les mesures appropriées, comme illustré dans l’exemple suivant.

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

Définition d’arguments d’entrée d’un flux de travail

Les données peuvent être transmises à un flux de travail lorsqu'il est démarré à l'aide d'un dictionnaire de paramètres, de la même manière que lors de l'utilisation de WorkflowInvoker. Chaque élément du dictionnaire est mappé à un argument d’entrée du flux de travail spécifié. Dans cet exemple, un flux de travail constitué d’une WriteLine activité est appelé et son Text argument est spécifié à l’aide du dictionnaire des paramètres d’entrée.

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

Récupération d’arguments de sortie d’un flux de travail

Lorsqu’un flux de travail est terminé, tous les arguments de sortie peuvent être récupérés dans le Completed gestionnaire en accédant au WorkflowApplicationCompletedEventArgs.Outputs dictionnaire. L’exemple suivant héberge un workflow à l’aide de WorkflowApplication. Une WorkflowApplication instance est construite à l’aide d’une définition de flux de travail composée d’une seule DiceRoll activité. L’activité DiceRoll a deux arguments de sortie qui représentent les résultats de l’opération de lancement de dés. Une fois le flux de travail terminé, les sorties sont récupérées dans le Completed gestionnaire.

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

Remarque

WorkflowApplication et WorkflowInvoker prennent un dictionnaire d’arguments d’entrée et retournent un dictionnaire d’arguments out. Ces paramètres de dictionnaire, propriétés et valeurs de retour sont de type IDictionary<string, object>. L'instance réelle de la classe de dictionnaire qui est passée peut être toute classe qui implémente IDictionary<string, object>. Dans ces exemples, Dictionary<string, object> est utilisé. Pour plus d’informations sur les dictionnaires, consultez IDictionary<TKey,TValue> et Dictionary<TKey,TValue>.

Passage de données dans un workflow en cours d'exécution à l'aide de signets

Les signets sont le mécanisme par lequel une activité peut attendre passivement la reprise et est un mécanisme permettant de transmettre des données à une instance de workflow en cours d’exécution. Si une activité attend des données, elle peut créer un Bookmark et enregistrer une méthode de rappel à appeler lorsque Bookmark est repris, comme illustré dans l'exemple suivant.

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

Lorsqu'elle est exécutée, l'activité ReadLine crée un Bookmark, enregistre un rappel, puis attend que Bookmark soit repris. Lorsqu'elle reprend, l'activité ReadLine attribue les données transmises avec le Bookmark à son argument Result. Dans cet exemple, un flux de travail est créé qui utilise l’activité ReadLine pour collecter le nom de l’utilisateur et l’afficher dans la fenêtre de console.

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

Lorsque l'activité ReadLine est exécutée, elle crée un Bookmark nommé UserName, puis attend la reprise du signet. L’hôte collecte les données souhaitées, puis reprend le Bookmark. Le flux de travail reprend, affiche le nom, puis se termine.

L’application hôte peut inspecter le flux de travail pour déterminer s’il existe des signets actifs. Pour ce faire, il peut appeler la méthode GetBookmarks d’une instance WorkflowApplication, ou inspecter WorkflowApplicationIdleEventArgs dans un gestionnaire Idle.

L’exemple de code suivant est semblable à l’exemple précédent, sauf que les signets actifs sont énumérés avant la reprise du signet. Le flux de travail est démarré, et une fois le Bookmark créé et le flux de travail devenu inactif, GetBookmarks est appelé. Une fois le flux de travail terminé, la sortie suivante s’affiche dans la console.

Quel est votre nom?
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());

L'exemple de code suivant inspecte le WorkflowApplicationIdleEventArgs passé au gestionnaire Idle d'une instance WorkflowApplication. Dans cet exemple, le flux de travail devenu inactif a un Bookmark nommé EnterGuess, qui appartient à une activité nommée ReadInt. Cet exemple de code est basé sur How to : Run a Workflow, qui fait partie du didacticiel de prise en main. Si le Idle gestionnaire de cette étape est modifié pour contenir le code de cet exemple, la sortie suivante s’affiche.

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

Résumé

WorkflowInvoker fournit un moyen léger d’appeler des flux de travail et, même s’il fournit des méthodes pour transmettre des données au début d’un flux de travail et extraire des données d’un flux de travail terminé, il ne fournit pas de scénarios plus complexes où WorkflowApplication il est possible d’utiliser.