Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Une définition de flux de travail est une arborescence d’objets d’activité configurés. Cette arborescence d’activités peut être définie de nombreuses façons, notamment en modifiant manuellement le code XAML ou en utilisant le Concepteur de flux de travail pour produire du code XAML. Toutefois, l’utilisation de XAML n’est pas requise. Les définitions de flux de travail peuvent également être créées par programmation. Cette rubrique fournit une vue d’ensemble de la création de définitions de flux de travail, d’activités et d’expressions à l’aide du code. Pour obtenir des exemples d’utilisation de flux de travail XAML à l’aide de code, consultez Sérialisation des flux de travail et des activités vers et depuis XAML.
Création de définitions de flux de travail
Une définition de flux de travail peut être créée en instanciant une instance d’un type d’activité et en configurant les propriétés de l’objet d’activité. Pour les activités qui ne contiennent pas d’activités enfants, cela peut être effectué à l’aide de quelques lignes de code.
Activity wf = new WriteLine
{
Text = "Hello World."
};
WorkflowInvoker.Invoke(wf);
Remarque
Les exemples de cette rubrique utilisent WorkflowInvoker pour exécuter les exemples de flux de travail. Pour plus d’informations sur l’appel de flux de travail, le passage d’arguments et les différents choix d’hébergement disponibles, consultez Utilisation de WorkflowInvoker et workflowApplication.
Dans cet exemple, un flux de travail constitué d’une seule WriteLine activité est créé. L’argument WriteLine de l’activité Text est défini et le flux de travail est appelé. Si une activité contient des activités enfants, la méthode de construction est similaire. L’exemple suivant utilise une Sequence activité qui contient deux WriteLine activités.
Activity wf = new Sequence
{
Activities =
{
new WriteLine
{
Text = "Hello"
},
new WriteLine
{
Text = "World."
}
}
};
WorkflowInvoker.Invoke(wf);
Utilisation d’initialiseurs d’objets
Les exemples de cette rubrique utilisent la syntaxe d’initialisation d’objet. La syntaxe d’initialisation d’objet peut être un moyen utile de créer des définitions de flux de travail dans le code, car elle fournit une vue hiérarchique des activités dans le flux de travail et montre la relation entre les activités. Il n’est pas nécessaire d’utiliser la syntaxe d’initialisation d’objet lorsque vous créez des flux de travail par programmation. L’exemple suivant équivaut fonctionnellement à l’exemple précédent.
WriteLine hello = new WriteLine();
hello.Text = "Hello";
WriteLine world = new WriteLine();
world.Text = "World";
Sequence wf = new Sequence();
wf.Activities.Add(hello);
wf.Activities.Add(world);
WorkflowInvoker.Invoke(wf);
Pour plus d’informations sur les initialiseurs d’objets, consultez Guide pratique pour initialiser des objets sans appeler un constructeur (Guide de programmation C#) et Comment : déclarer un objet à l’aide d’un initialiseur d’objet.
Utilisation de variables, de valeurs littérales et d’expressions
Lors de la création d’une définition de flux de travail à l’aide du code, sachez quel code s’exécute dans le cadre de la création de la définition de flux de travail et du code exécuté dans le cadre de l’exécution d’une instance de ce flux de travail. Par exemple, le flux de travail suivant est destiné à générer un nombre aléatoire et à l’écrire dans la console.
Variable<int> n = new Variable<int>
{
Name = "n"
};
Activity wf = new Sequence
{
Variables = { n },
Activities =
{
new Assign<int>
{
To = n,
Value = new Random().Next(1, 101)
},
new WriteLine
{
Text = new InArgument<string>((env) => "The number is " + n.Get(env))
}
}
};
Lorsque ce code de définition de flux de travail est exécuté, l’appel à Random.Next est effectué et le résultat est stocké dans la définition de flux de travail sous la forme d’une valeur littérale. De nombreuses instances de ce flux de travail peuvent être appelées et toutes affichent le même nombre. Pour que la génération de nombres aléatoires se produise pendant l’exécution du flux de travail, une expression doit être utilisée qui est évaluée chaque fois que le flux de travail s’exécute. Dans l’exemple suivant, une expression Visual Basic est utilisée avec un VisualBasicValue<TResult>.
new Assign<int>
{
To = n,
Value = new VisualBasicValue<int>("New Random().Next(1, 101)")
}
L’expression de l’exemple précédent peut également être implémentée à l’aide d’une CSharpValue<TResult> et d’une expression C#.
new Assign<int>
{
To = n,
Value = new CSharpValue<int>("new Random().Next(1, 101)")
}
Les expressions C# doivent être compilées avant l’appel du flux de travail contenant ces expressions. Si les expressions C# ne sont pas compilées, une NotSupportedException est levée lorsque le flux de travail est appelé avec un message similaire à ce qui suit : Expression Activity type 'CSharpValue`1' requires compilation in order to run. Please ensure that the workflow has been compiled. Dans la plupart des scénarios impliquant des flux de travail créés dans Visual Studio, les expressions C# sont compilées automatiquement, mais dans certains scénarios, comme les flux de travail de code, les expressions C# doivent être compilées manuellement. Pour obtenir un exemple de compilation d’expressions C#, consultez la section Utilisation d’expressions C# dans les flux de travail de code de la rubrique Expressions C# .
Un VisualBasicValue<TResult> représente une expression dans la syntaxe Visual Basic qui peut être utilisée comme valeur r dans une expression, et un CSharpValue<TResult> représente une expression dans la syntaxe C# qui peut être utilisée comme valeur r dans une expression. Ces expressions sont évaluées chaque fois que l’activité où elles se trouvent est exécutée. Le résultat de l’expression est affecté à la variable nde flux de travail, et ces résultats sont utilisés par l’activité suivante dans le flux de travail. Pour accéder à la valeur de la variable n de flux de travail au moment de l’exécution, la ActivityContext valeur est requise. Vous pouvez y accéder à l’aide de l’expression lambda suivante.
Remarque
Notez que ces deux codes sont des exemples qui utilisent C# comme langage de programmation, mais qu’on utilise un VisualBasicValue<TResult> et un utilise un CSharpValue<TResult>. VisualBasicValue<TResult> et CSharpValue<TResult> peut être utilisé dans les projets Visual Basic et C#. Par défaut, les expressions créées dans le concepteur de flux de travail correspondent au langage du projet d’hébergement. Lors de la création de flux de travail dans le code, la langue souhaitée est à la discrétion de l’auteur du flux de travail.
Dans ces exemples, le résultat de l’expression est affecté à la variable nde flux de travail, et ces résultats sont utilisés par l’activité suivante dans le flux de travail. Pour accéder à la valeur de la variable n de flux de travail au moment de l’exécution, la ActivityContext valeur est requise. Vous pouvez y accéder à l’aide de l’expression lambda suivante.
new WriteLine
{
Text = new InArgument<string>((env) => "The number is " + n.Get(env))
}
Pour plus d’informations sur les expressions lambda, consultez Expressions lambda (référence C#) ou Expressions lambda (Visual Basic).
Les expressions lambda ne sont pas sérialisables au format XAML. Si une tentative de sérialiser un workflow avec les expressions lambda est faite, LambdaSerializationException est levée avec le message suivant : « Ce workflow contient des expressions lambda spécifiées dans le code. Ces expressions ne sont pas sérialisables XAML. Pour rendre votre workflow XAML sérialisable, utilisez VisualBasicValue/VisualBasicReference ou ExpressionServices.Convert(lambda). Cela convertit vos expressions lambda en activités d’expression. » Pour rendre cette expression compatible avec XAML, utilisez ExpressionServices et Convert, comme illustré dans l’exemple suivant.
new WriteLine
{
//Text = new InArgument<string>((env) => "The number is " + n.Get(env))
Text = ExpressionServices.Convert((env) => "The number is " + n.Get(env))
}
Un VisualBasicValue<TResult> peut également être utilisé. Notez qu’aucune expression lambda n’est requise lors de l’utilisation d’une expression Visual Basic.
new WriteLine
{
//Text = new InArgument<string>((env) => "The number is " + n.Get(env))
//Text = ExpressionServices.Convert((env) => "The number is " + n.Get(env))
Text = new VisualBasicValue<string>("\"The number is \" + n.ToString()")
}
Au moment de l’exécution, les expressions Visual Basic sont compilées en expressions LINQ. Les deux exemples précédents sont sérialisables en XAML, mais si le code XAML sérialisé est destiné à être consulté et modifié dans le concepteur de flux de travail, utilisez-le VisualBasicValue<TResult> pour vos expressions. Les flux de travail sérialisés qui utilisent ExpressionServices.Convert peuvent être ouverts dans le concepteur, mais la valeur de l’expression est vide. Pour plus d’informations sur la sérialisation des flux de travail en XAML, consultez Sérialisation des flux de travail et des activités vers et depuis XAML.
Expressions littérales et types de référence
Les expressions littérales sont représentées dans les flux de travail par l’activité Literal<T> . Les activités suivantes WriteLine sont fonctionnellement équivalentes.
new WriteLine
{
Text = "Hello World."
},
new WriteLine
{
Text = new Literal<string>("Hello World.")
}
Il n’est pas valide d’initialiser une expression littérale avec n’importe quel type de référence, sauf String. Dans l'exemple suivant, la propriété Assign d'une activité Value est initialisée avec une expression littérale à l'aide de List<string>.
new Assign
{
To = new OutArgument<List<string>>(items),
Value = new InArgument<List<string>>(new List<string>())
},
Lorsque le flux de travail contenant cette activité est validé, l’erreur de validation suivante est retournée : « Le littéral prend uniquement en charge les types valeur et le type immuable System.String. Le type System.Collections.Generic.List'1[System.String] ne peut pas être utilisé comme littéral. » Si le workflow est appelé, une InvalidWorkflowException est levée qui contient le texte de l’erreur de validation. Il s’agit d’une erreur de validation, car la création d’une expression littérale avec un type référence ne crée pas d’instance du type de référence pour chaque instance du flux de travail. Pour résoudre ce problème, remplacez l’expression littérale par une expression qui crée et retourne une nouvelle instance du type de référence.
new Assign
{
To = new OutArgument<List<string>>(items),
Value = new InArgument<List<string>>(new VisualBasicValue<List<string>>("New List(Of String)"))
},
Pour plus d’informations sur les expressions, consultez Expressions.
Appel de méthodes sur des objets à l’aide d’expressions et de l’activité InvokeMethod
L’activité InvokeMethod<TResult> peut être utilisée pour appeler des méthodes statiques et d’instance de classes dans le .NET Framework. Dans un exemple précédent de cette rubrique, un nombre aléatoire a été généré à l’aide de la Random classe.
new Assign<int>
{
To = n,
Value = new VisualBasicValue<int>("New Random().Next(1, 101)")
}
L’activité InvokeMethod<TResult> a également pu être utilisée pour appeler la Next méthode de la Random classe.
new InvokeMethod<int>
{
TargetObject = new InArgument<Random>(new VisualBasicValue<Random>("New Random()")),
MethodName = "Next",
Parameters =
{
new InArgument<int>(1),
new InArgument<int>(101)
},
Result = n
}
Comme Next il ne s’agit pas d’une méthode statique, une instance de la Random classe est fournie pour la TargetObject propriété. Dans cet exemple, une nouvelle instance est créée à l’aide d’une expression Visual Basic, mais elle peut également avoir été créée précédemment et stockée dans une variable de flux de travail. Dans cet exemple, il serait plus simple d’utiliser l’activité Assign<T> au lieu de l’activité InvokeMethod<TResult> . Si l'appel de la méthode appelée en dernier lieu par l'activité Assign<T> ou InvokeMethod<TResult> est long, InvokeMethod<TResult> présente un avantage, car il a une propriété RunAsynchronously. Lorsque cette propriété est définie truesur , la méthode appelée s’exécute de manière asynchrone en ce qui concerne le flux de travail. Si d’autres activités sont en parallèle, elles ne seront pas bloquées pendant l’exécution asynchrone de la méthode. En outre, si la méthode à appeler n’a aucune valeur de retour, InvokeMethod<TResult> il s’agit de la façon appropriée d’appeler la méthode.
Arguments et activités dynamiques
Une définition de flux de travail est créée dans le code en assemblant des activités dans une arborescence d’activités et en configurant toutes les propriétés et arguments. Les arguments existants peuvent être liés, mais de nouveaux arguments ne peuvent pas être ajoutés aux activités. Cela inclut les arguments de workflow passés à l’activité racine. Dans le code impératif, les arguments de flux de travail sont spécifiés en tant que propriétés sur un nouveau type CLR, et en XAML, ils sont déclarés à l’aide x:Class et x:Member. Étant donné qu’il n’existe aucun nouveau type CLR créé lorsqu’une définition de flux de travail est créée en tant qu’arborescence d’objets en mémoire, les arguments ne peuvent pas être ajoutés. Toutefois, les arguments peuvent être ajoutés à un DynamicActivity. Dans cet exemple, un DynamicActivity<TResult> est créé qui prend deux arguments entiers, les additionne et en retourne le résultat. Un DynamicActivityProperty est créé pour chaque argument et le résultat de l’opération est affecté à l’argument Result du DynamicActivity<TResult>.
InArgument<int> Operand1 = new InArgument<int>();
InArgument<int> Operand2 = new InArgument<int>();
DynamicActivity<int> wf = new DynamicActivity<int>
{
Properties =
{
new DynamicActivityProperty
{
Name = "Operand1",
Type = typeof(InArgument<int>),
Value = Operand1
},
new DynamicActivityProperty
{
Name = "Operand2",
Type = typeof(InArgument<int>),
Value = Operand2
}
},
Implementation = () => new Sequence
{
Activities =
{
new Assign<int>
{
To = new ArgumentReference<int> { ArgumentName = "Result" },
Value = new InArgument<int>((env) => Operand1.Get(env) + Operand2.Get(env))
}
}
}
};
Dictionary<string, object> wfParams = new Dictionary<string, object>
{
{ "Operand1", 25 },
{ "Operand2", 15 }
};
int result = WorkflowInvoker.Invoke(wf, wfParams);
Console.WriteLine(result);
Pour plus d’informations sur les activités dynamiques, consultez Création d’une activité au moment de l’exécution.
Activités compilées
Les activités dynamiques sont une façon de définir une activité qui contient des arguments à l’aide du code, mais les activités peuvent également être créées dans le code et compilées en types. Les activités simples peuvent être créées qui dérivent de CodeActivity, et les activités asynchrones qui dérivent de AsyncCodeActivity. Ces activités peuvent avoir des arguments, des valeurs de retour et définir leur logique à l’aide du code impératif. Pour obtenir des exemples de création de ces types d’activités, consultez la classe de base CodeActivity et la création d’activités asynchrones.
Les activités qui dérivent de NativeActivity peuvent définir leur logique en utilisant du code impératif et elles peuvent également contenir des activités enfant qui définissent la logique. Ils ont également un accès complet aux fonctionnalités du runtime, telles que la création de signets. Pour obtenir des exemples de création d'une activité fondée sur NativeActivity, consultez la documentation NativeActivity Base Class, Comment créer une activité et l'exemple Custom Composite utilisant Native Activity.
Les activités qui dérivent de Activity définissent leur logique uniquement via l'utilisation d'activités enfants. Ces activités sont généralement créées à l’aide du concepteur de flux de travail, mais peuvent également être définies à l’aide du code. Dans l’exemple suivant, une Square activité est définie à partir de Activity<int>. L’activité Square a un InArgument<T> nommé Value et définit sa logique en spécifiant une activité Sequence à l'aide de la propriété Implementation. L’activité Sequence contient une WriteLine activité et une Assign<T> activité. Ensemble, ces trois activités implémentent la logique de l’activité Square .
class Square : Activity<int>
{
[RequiredArgument]
public InArgument<int> Value { get; set; }
public Square()
{
this.Implementation = () => new Sequence
{
Activities =
{
new WriteLine
{
Text = new InArgument<string>((env) => "Squaring the value: " + this.Value.Get(env))
},
new Assign<int>
{
To = new OutArgument<int>((env) => this.Result.Get(env)),
Value = new InArgument<int>((env) => this.Value.Get(env) * this.Value.Get(env))
}
}
};
}
}
Dans l'exemple suivant, une définition de workflow composée d'une seule activité Square est invoquée à l'aide de WorkflowInvoker.
Dictionary<string, object> inputs = new Dictionary<string, object> {{ "Value", 5}};
int result = WorkflowInvoker.Invoke(new Square(), inputs);
Console.WriteLine("Result: {0}", result);
Lorsque le flux de travail est appelé, la sortie suivante s’affiche dans la console :
Quar la valeur : 5Résultat : 25