In the previous article, use the workflow designer to design a fairly simple workflow. Now, we will use the encoding method to implement the same workflow as the previous one. Any process can be usedCodeYou can also use the workflow designer. The method to choose is the question of the benevolent and wise. However, coding helps you better understand how the process works.
Create a console applicationProgram
Start Vs and create a simple console application (note that the workflow template is not used here), as shown in Figure 2-1.
Figure 2-1.Create a console application
Add system. Activities Reference. In this way, you can use the wf4 activity in this console application. Use the following code to replace the namespace on the program. CS file.
UsingSystem;UsingSystem. Activities;UsingSystem. Activities. statements;UsingSystem. Activities. expressions;
Enter the following code in the main () function:
Workflowinvoker. Invoke (createworkflow (); console. writeline ("Press enter to exit"); Console. Readline ();
The above is the same as the implementation of the main () function in the previous article. If you want to, you can simply copy and paste the code of the previous application. However, there is a difference between them. The following code calls createworkflow (), while the previous one calls workflow1 ().
Workflowinvoker. Invoke (createworkflow ());
Workflow1 generated by the workflow designer is defined in the workflow1.xaml file. Createworkflow () is a method you will implement.
Define Workflow
As I mentioned in the previous article, a workflow is just a set of nested attributes. More accurately, a workflow is a set of Nested classes and their attributes. To simplify this process, I will demonstrate how to implement them at a level. First, add the following method to the program. CS file:
StaticActivity createworkflow () {Variable <Int> Numberbells =NewVariable <Int> () {Name ="Numberbells", Default = datetime. Now. hour}; variable <Int> Counter =NewVariable <Int> () {Name ="Counter", Default = 1 };Return NewSequence (){};}
The createworkflow () method first creates two variable int types: numberbells and counter. They are available for various activities.
The createworkflow () method returns an activity type activity. The activity type is the parameter type required for the workflowinvoker class invoke method. Activity is the base class of all activities, including sequence activities.
Therefore, the sequence instance is returned, while the return type of the createworkflow () method is activity.
Implementation level 1
Now you have defined an empty sequence activity. It is equivalent to creating an ordered workflow without sub-activities. Now, add several sub-activities to sequence and use the code in Listing 2-1 to replace return new sequence ()
Return New Sequence () {displayname =" Main sequence ", Variables = {numberbells, counter}, activities = { New Writeline () {displayname =" Hello ", Text =" Hello, world! "}, New If () {displayname =" Adjust for PM " // Code to be added here in Level 2 },New While () {displayname =" Sound bells " // Code to be added here in Level 2 }, New Writeline () {displayname =" Display time ", Text =" The time is: "+ Datetime. Now. tostring ()}, New If () {displayname =" Greeting " // Code to be added here in Level 2 }}};
This code first defines the displayname attribute of the sequence activity and the variable object related to the sequence. Then, initialize the set of sub-activity members. Specifically, it creates sub-activities such as Table 2-1.
The text attribute is also defined for writeline activities. The specific implementation of other activities will be defined at the next level.
Implementation Level 2
First, enter the following code for the if activity:
Displayname ="Adjust for PM",// Code to be added here in Level 2Condition = expressionservices. Convert <Bool> (ENV => numberbells. Get (ENV)> 12), then =NewAssign <Int> () {Displayname ="Adjust bells"// Code to be added here in level 3}
This Code defines the condition attribute and then attribute of the I activity (the else branch is not defined ). The assign activity is implemented in the next layer. Description of the condition attribute.
Expressions
The static convert <t> () method of the expressionservices class is used to create an inargument <t> class. It is the type required by the condition attribute. These classes and methods use generics (<t> ). So they can use any data type. Here, we use the bool type. Because the value of the IF activity's condition attribute is neither true nor false. This type of expression is implemented through a Lambda expression (similar to the LINQ syntax) to obtain data from the runtime environment of the workflow. In a Lambda expression, => is a Lambda operator. The input parameter is on the left, and the actual expression is placed on the right of the lambda operator. Env value is supported during runtime, when condition is determined.
A workflow is actually stateless and does not store data elements. The variable class is a simple definition of data types. To obtain actual data from a variable, you will use its get () method. This also requires an activitycontext class object. This is used to differentiate specific workflow instances because some other workflow instances are currently running. Compare whether the returned value of get (ENV) is greater than 12.
Enter the following code in the while activity:
Displayname ="Sound bells",// Code to be added here in Level 2Condition = expressionservices. Convert <Bool> (ENV => counter. Get (ENV) <= numberbells. Get (ENV), body =NewSequence () {displayname ="Sound bell"// Code to be added here in level 3}
The while activity's condition attribute is the same as the if activity's condition attribute. It uses the expressionservices class to create an inargument <t> class object with the bool type. In this example, it is used to determine whether count <= numberbells is true. For these two variables, use the get (ENV) method to obtain the actual value. In the second if activity ("greeting"), enter the following code:
Displayname ="Greeting",// Code to be added here in Level 2Condition = expressionservices. Convert <Bool> (ENV => datetime. Now. Hour> = 18), then =NewWriteline () {text ="Good evening"}, Else =NewWriteline () {text ="Good day"}
Although the input parameter env is not used in this condition, it must be declared in this expression. The logic is to determine whether the current time exceeds. Add a writeline activity to the then and else attributes. One outputs "Good evening" and the other outputs "good day ".
Implementation Level 3:
For the first if activity (named "adjust for PM"), you have added an empty assign activity to its then attribute. Enter the following implementation code in the assign activity:
Displayname ="Adjust bells",
// Code to be added here in level 3
To =NewOutargument <Int> (Numberbells ),
Value =NewInargument <Int> (ENV => numberbells. Get (ENV)-12)
Assign activity:
The assign activity is generic and supports any data type. In this example, it is used to assign an integer, so it creates an assign activity of the assign <int> type. The to and value attributes also use this template class, so they should be of the same type (<int> ). The type of the to attribute is an outargument, and a variable object is passed in its constructor. The Value Attribute requires an inargument object. Use this activity before the condition attribute of IF and while. Like the condition attribute, lambda expressions are used in its constructor ,.
Sequence
In the while activity, you have created an empty sequence activity. The while loop is iterated once, and the sub-activities in the sequence activity will be executed once. Enter the following code to fill in the activities attribute:
Displayname ="Sound bell",// Code to be added here in level 3Activities = {NewWriteline () {text =NewInargument <String> (ENV => counter. Get (ENV). tostring ())},NewAssign <Int> () {Displayname ="Increment counter", To =NewOutargument <Int> (Counter), value =NewInargument <Int> (ENV => counter. Get (ENV) + 1 )},NewDelay () {duration = timespan. fromseconds (1 )}}
This Code adds three activities to sequence:
Writeline activity is used to display counter
The assign activity is used to increase the Count of counters.
The delay activity is used to pause the process for a period of time in a loop.
The text attribute of the writeline activity is different from that of other writeline activities. It is not a pure string. The text attribute in this writeline activity is defined as an expression. Because the text property requires a string type, we create an inargument <string> class for it. Now, you may be used to these lambda expressions. The get (ENV) method of the variable class returns an int type value. The tostring () method converts it to a string. A timespan object created by the static method fromseconds () is passed to the Duration Attribute of the delay activity.
Run the application
Press F5 to run the application. The result depends on the time of the day. You should look like this:
Hello, world!
1
2
3
4
5
6
7
The time is: 10/5/2009 7:02:41 pm
Good evening
Press enter to exit
The complete implementation code of program. CS is shown in Listing 2-2.
List 2-2.program.cs
Using System; Using System. Activities;Using System. Activities. statements; Using System. Activities. expressions; Namespace Chapter02 { Class Program { Static Void Main ( String [] ARGs) {workflowinvoker. Invoke (createworkflow (); console. writeline (" Press enter to exit "); Console. Readline ();} Static Activity createworkflow () {Variable < Int > Numberbells = New Variable < Int > () {Name =" Numberbells ", Default = datetime. Now. hour}; variable < Int > Counter = New Variable < Int > () {Name =" Counter ", Default = 1 }; Return New Sequence () {displayname =" Main sequence ", Variables = {numberbells, counter}, activities = { New Writeline () {displayname =" Hello ",}, New If () {displayname =" Adjust for PM ", // Code to be added here in Level 2 Condition = expressionservices. Convert < Bool > (ENV => numberbells. Get (ENV)> 12), then = New Assign < Int > () {Displayname =" Adjust bells ", // Code to be added here in level 3 To = New Outargument < Int > (Numberbells), value =New Inargument < Int > (ENV => numberbells. Get (ENV)-12 )}}, New While () {displayname =" Sound bells ", // Code to be added here in Level 2 Condition = expressionservices. Convert < Bool > (ENV => counter. Get (ENV) <= numberbells. Get (ENV), body = New Sequence () {displayname =" Sound bell ", // Code to be added here in level 3 Activities = { New Writeline () {text = New Inargument < String > (ENV => counter. Get (ENV). tostring ())}, New Assign < Int > () {Displayname =" Increment counter ", To = New Outargument < Int > (Counter), value = New Inargument < Int > (ENV => counter. Get (ENV) + 1 )}, New Delay () {duration = timespan. fromseconds (1 )}}}},New Writeline () {displayname =" Display time ", Text =" The time is: "+ Datetime. Now. tostring ()}, New If () {displayname =" Greeting ", // Code to be added here in Level 2 Condition = expressionservices. Convert < Bool > (ENV => datetime. Now. Hour> = 16), then = New Writeline () {text =" Good evening "}, Else = New Writeline () {text ="Good day "}}}};}}}
Review
Some examples in this book are implemented using the designer, while others are implemented using the encoding method. When you start learning, using the designer may be easier to accept and understand the workflow than using encoding. However, if you are familiar with the workflow, you may find that the encoding method is faster. The final results of the two methods are the same, and both methods are very good.