Workflow Foundation 4.0 Collections
Introduction
In this post we will see an important built in activities comes with Workflow foundation 4.0., these built-in activities enable you to manipulate a collection in your workflow. this post guide you to do the most required actions for manipulating collections like add,remove and delete items from collection and sorting, searching and clearing the collection.
We will build a console application that demonstrates these activities using a Book Store application.
Building Book Store application
To build the Book Store application you should follow the below steps
1- Create new console application and add System.Activities reference to the new project
2- Open the program.cs file and add the following list of code for declaring BookStoreListItem this class contains a few public properties that store information about an item to be stored.
1: public class BookStoreListItem
2: {
3: public string Description { get; set; }
4: public int Quantity { get; set; }
5: public decimal UnitPrice { get; set; }
6: public string Author { get; set; }
7: public string BookName { get; set; }
8: public BookStoreListItem(string description, int quantity, decimal unitPrice,string author, string bookName)
9: {
10: Description = description;
11: Quantity = quantity;
12: UnitPrice = unitPrice;
13: BookName = bookName;
14: Author = author;
15: }
16: public BookStoreListItem(string bookName)
17: {
18: BookName= bookName;
19: }
20: }
3- Initiate the Workflow by adding the following namespace to the top of program.cs class
using System.Activities.Expressions; now add the following list of code after the closed practices for the main method in the program.cs class
1: private static Activity CollectionWF()
2: {
3: // myList is a collection of ListItem objects
4: Variable<ICollection<BookStoreListItem>> myList = new Variable<ICollection<BookStoreListItem>>()
5: {
6: Name = "MyList",
7: Default = new LambdaValue<ICollection<BookStoreListItem>>
8: (env => new List<BookStoreListItem>())
9: };
10: return new Sequence
11: {
12: Variables = { myList },
13: Activities =
14: {
15: new WriteLine
16: {
17: Text = "Workflow starting..."
18: },
19: new AddToCollection<BookStoreListItem>
20: {
21: Collection = myList,
22: Item = new LambdaValue<BookStoreListItem>
23: (env => new BookStoreListItem("New WorkFlow 4.0 book", 5, 50,"Mohammed", "WorkFlow 4.0"))
24: },
25: new AddToCollection<BookStoreListItem>
26: {
27: Collection = myList,
28: Item = new LambdaValue<BookStoreListItem>
29: (env => new BookStoreListItem("New Linq 4.0 book", 3, 30,"Sherif", "LINQ 4.0"))
30: },
31: new AddToCollection<BookStoreListItem>
32: {
33: Collection = myList,
34: Item = new LambdaValue<BookStoreListItem>
35: (env => new BookStoreListItem("New Windows Communication Foundation 4.0 book", 8, 80,"Atef", "WCF 4.0"))
36: },
37: new AddToCollection<BookStoreListItem>
38: {
39: Collection = myList,
40: Item = new LambdaValue<BookStoreListItem>
41: (env => new BookStoreListItem("Visual studio 2010 book", 15, 40,"Khalid", "VS 2010"))
42: },
43: new WriteLine
44: {
45: Text = "Workflow ended"
46: }
47: }
48: };
49: }
This workflow defines a variable that is a collection of BookStoreListItem classes named myList. The variable can be any typeof collection that supports the ICollection interface.the LambdaValue Represents a lambda expression used as an r-value, which supports binding of In arguments so it is bind the default value of myList with the returned new instance of BookStoreListItem class .
After that we are going to build the sequence activity for our workflow and the sequence activity will have the following activities
a- writeline activity that printing “Workflow starting…”
b- four AddToCollection activity that used for adding new item of BookStoreListItem to myList
c- another writeline activity to print “Workflow ended…”
4- Now we need to invoke the workflow, so please add the following lines of code in the main method at program.cs class
1: WorkflowInvoker.Invoke(CollectionWF());
2: Console.WriteLine("Press ENTER to exit");
3: Console.ReadLine();
These few line of code used to invoke the workflow by calling collectionWF into the Invoke method.
If you have run the application now you will find the following lines printed
Workflow starting…
Workflow ended…
Press ENTER to exit
5- Printing Collection , we will provide a way to display the contents of the books list, to do that right-click the project and choose Add new Item and select Class, For the class name, enter PrintBooksList.cs. The implementation of this class is shown in Listing below
1: using System;
2: using System.Activities;
3: using System.Collections.Generic;
4: namespace WFFBookStoreApp
5: {
6: public sealed class PrintBooksList: CodeActivity
7: {
8: public InArgument<ICollection<BookStoreListItem>> Collection { get; set; }
9:
10: protected override void Execute(CodeActivityContext context)
11: {
12: ICollection<BookStoreListItem> list =
13: this.Collection.Get<ICollection<BookStoreListItem>>(context);
14: if (list.Count == 0)
15: {
16: Console.WriteLine("The Book list is empty");
17: }
18: else
19: {
20: foreach (BookStoreListItem l in list)
21: {
22: Console.WriteLine("Book Named {0} has the following information :", l.BookName);
23: Console.WriteLine("(Description:{0} ,Quantity: {1},Price: {2} ,Author {3})",
24: l.Description,l.Quantity.ToString(), l.UnitPrice, l.Author);
25: }
26: Console.WriteLine();
27: }
28: }
29: }
30: }
This custom activity receives the collection as an input argument. It expects a collection of BookStoreListItem classes. the overridden execute method first check if the list empty or not, if yes it is printing the book list is empty message, else it is loop throw the list and print each book item information, this code is very simple you can understand but if you do not know how to create custom activity please visit the following link WFF 4.0 custom activity.
6- To use this custom activity, Open the program.cs file and add the following activity definition to the collectionWF () method before the final writeline activity
1: new PrintBooksList
2: {
3: Collection=myList
4: },
If you run the application now you will find the following message printed
7- Sorting the collection, we will provide a way to sort the books list by book price, to do that right-click the project and choose Add new Item and select Class, For the class name, enter SortBooksList.cs. The implementation of this class is shown in Listing below
1: using System;
2: using System.Activities;
3: using System.Collections.Generic;
4: namespace WFFBookStoreApp
5: {
6: public sealed class SortBooksList : CodeActivity
7: {
8: public InOutArgument<ICollection<BookStoreListItem>> Collection { get; set; }
9: protected override void Execute(CodeActivityContext context)
10: {
11: ICollection<BookStoreListItem> tempList = this.Collection.Get<ICollection<BookStoreListItem>>(context);
12: if (tempList.Count > 0)
13: {
14: List<BookStoreListItem> sortedList = new List<BookStoreListItem>(tempList);
15: ItemComparer c = new ItemComparer();
16: sortedList.Sort(c as IComparer<BookStoreListItem>);
17: Collection.Set(context, sortedList as ICollection<BookStoreListItem>);
18: }
19: }
20: }
21: public class ItemComparer : IComparer<BookStoreListItem>
22: {
23: public int Compare(BookStoreListItem x, BookStoreListItem y)
24: {
25: // Handle null arguments
26: if (x == null && y == null)
27: return 0;
28: if (x == null)
29: return -1;
30: if (y == null)
31: return 1;
32: // Perform comparison based on the priority
33: if (x.UnitPrice == y.UnitPrice)
34: return 0;
35: if (x.UnitPrice > y.UnitPrice)
36: return 1;
37: else
38: return -1;
39: }
40: }
41: }
This custom activity receives the collection as an input argument and return the same argument after sorting it. It expects a collection of BookStoreListItem classes .the overridden Execute() method of this activity takes a collection as both an input and output argument.The Sort() method of the List class is used to perform the sort, but you must supply a class that implements the IComparer interface because the standard implementation will not know how to sort BookStoreListItem objects. The IComparer interface provides a single method called Comparer (), which receives two objects (x and y) as input parameters. It returns 0 if the two objects are equal, 1 if x is greater than y, and -1 if x is less than y.
8- To use this custom activity, Open the Program.cs file and add the following activity definition to the collectionWF () method before the final writeline activity
1: new SortBooksList
2: {
3: Collection=myList
4: },
5: new WriteLine
6: {
7: Text = "Books List sorted by Price"
8: },
9: new PrintBooksList
10: {
11: Collection=myList
12: },
If you run the application now you will find the following message printed
if you have reviewed the printed lines in console, you will find the new books listed after line “books list sorted by price are order desc by price.
9- Searching in the collection, now we are going to use built in activity in Workflow Foundation 4.0 like ExistsInCollection, so we will check if the item found in the book list or not.
As the ExistsInCollection should iterate between books list items so it is require to use Equals() methods which mean we need to implement this method to our BookStoreListItem class, For our purposes, you should consider two items equal if they have the same name,To do that add the following list of code to BookStoreListItem class in program.cs file
1: public override bool Equals(object obj)
2: {
3: BookStoreListItem i = obj as BookStoreListItem;
4: if (i == null)
5: return false;
6: else
7: {
8: if (i.BookName == this.BookName)
9: return true;
10: else
11: return false;
12: }
13: }
14: public override int GetHashCode()
15: {
16: return base.GetHashCode();
17: }
The Equals() method cast the input object to a BookStoreListItem and returns True if the BookName property matches. When overriding the Equals() method, the compiler expects you to also override the GetHashCode() method. This implementation simply calls the base method
10- Now , we can use ExistsInCollection to do that add the following lines of code before the final writeline activity
1: public override bool Equals(object obj)
2: {
3: BookStoreListItem i = obj as BookStoreListItem;
4: if (i == null)
5: return false;
6: else
7: {
8: if (i.BookName == this.BookName)
9: return true;
10: else
11: return false;
12: }
13: }
14: public override int GetHashCode()
15: {
16: return base.GetHashCode();
17: }
18:
If you have run the application now you will see this line printed before workflow ended
“LINQ 4.0 Book has been added before”
11- Remove item from Collection, Workflow Foundation 4.0 has activity named RemoveFromCollection that is responsible for removing item from collection it very easy in implementation, we will use it to remove book named “LINQ 4.0“,to do that add the following line of code before the final writeline activity
1: new WriteLine
2: {
3: Text = "Removing LINQ 4.0 book from Books Store..."
4: },
5: new RemoveFromCollection<BookStoreListItem>()
6: {
7: Collection = myList,
8: Item = new LambdaValue<BookStoreListItem>
9: (env => new BookStoreListItem("LINQ 4.0"))
10: },
11: new PrintBooksList()
12: {
13: Collection = myList
14: },
if you have run the application now you will see the following lines printed before workflow ended line
12- Clearing the collection, Workflow Foundation 4.0 has built in activity to clear collection named ClearCollection to use this activity add the following lines of code before the final writeline activity
1: new ClearCollection<BookStoreListItem>()
2: {
3: Collection = myList
4: },
5: new PrintBooksList()
6: {
7: Collection = myList
8: },
If you have run the application now you will find line printed before workflow ended that says
“The book list is Empty”
Conclusion:
I was trying to learn you how WFF 4.0 are manipulation with collection,you can download The complete implementation of that post from here and you can return back to workflow posts list here
Back to other Workflow Foundation 4.0 posts
I Hope that helped
Workflow Foundation 4.0 Custom activities
WFF 4.0 helps you to implement you own custom activity. sure creating custom activity is very important part that all Workflow developers are interested in.the new Workflow Foundation 4.0 give you two ways of generating custom activities.
As we need to prove concept only, my custom activity will be simple, just I am going to build custom activity that print the message “Hello FirstName Last Name” this custom activity taking two input parameters First Name and Last Name and return the full message,let’s see how to implement this simple example by Workflow Foundation 4.0.
Using Code Activity template
Code activity template give you advantage to inherit the codeactivity class and build your custom activity from scratch but the new Workflow Foundation helps you to do that in easy way, so to implement the new custom activity follow the following steps
1- Open Visual Studio 2010 and select file->new project->workflow->workflow consol application
2- In the name textbox write CustomactivitySample as the project name
3- After the solution has opened right click in the project name and select add new item that will open the list of items as shown in the below picture
select code activity and enter code activity name such as “HelloMsg“
4- By default you with find the below list of code has been generated in your new file that named HelloMsg.cs
1: public sealed class HelloMsg : CodeActivity
2: {
3: // Define an activity input argument of type string
4: public InArgument<string> Text { get; set; }
5: // If your activity returns a value, derive from CodeActivity<TResult>
6: // and return the value from the Execute method.
7: protected override void Execute(CodeActivityContext context)
8: {
9: // Obtain the runtime value of the Text input argument
10: string text = context.GetValue(this.Text);
11: }
12: }
5- Now we are going to add our code for printing hello message
- First we will prepare the activity parameters, so replace line no 4 in the above codes list with the following list of codes
1: public InArgument<string> FirstName { get; set; }2: public InArgument<string> LastName { get; set; }3: public OutArgument<string> HelloMessgae { get; set; }As shown in the above codes list we have declared two input parameters first name and last name and one output parameter for returning the complete hello message.
- The last step for implementing this custom activity is to add code for using input parameters and concatenating it to return the complete hello message, so please replace line 10 at the codes list in step 4 with the following list of codes
1: string fname = context.GetValue(this.FirstName);2: string lname = context.GetValue(this.LastName);3: string completeMsg = " Hello " + fname +" "+ lname;4: context.SetValue(this.HelloMessgae, completeMsg);
6- Build the project to refresh the toolbar, and you will notice new activity hellomsg activity has been added to the toolbar.
7- Open Workflow1.xaml in designer view and drag sequence activity then drag hellomsg and writeline activity by ordered in the new added sequence activity
8- Create variable named fullname and has data type string from the variables window at bottom of the workflow1 designer
9-Open the writeline property windows and set the text property to the new created variable name in the previous step
10- Open the hellomsg property window and set it is properties as follow:
a- FirstName: “Mohammed”
b- Last Name: “Atef”
c- HelloMessage: fullname which added in step no 8
Finally run the application you will see this message in the console window
“Hello Mohammed Atef”
Using Activity
Using the activity you can create your own activity and use it in another work flow as independent activity and you can pass parameters between your activity and the main workflow designer
This kind of activities is very simple as you are using the workflow designer with all toolbar activities available to implement you custom activity.
We are going to do the same business scenario in the previous example but we will add the following message before the hello message “Custom Activity using Designer.” this only to determine the message printed for each custom activity.
Now , to implement the new custom activity follow the following steps
1- Write click into the project name and select add new item then select activity icon from the windows as shown in picture below
name that activity hellomsgActivity.xaml
2- Drag sequence activity to the new created activity named hellomsgActivity.xaml and then drag writeline activity into the sequence activity
3- Add two arguments from arguments window below the designer of that activity named hellomsgActivity.xaml
4- Write the following line into the text property of the writeline activity
“Custom Activity using Designer: Hello Mr. ” + FName + LName
5- Build the application
6- Open the main workflow1.xaml in design mode and drag hellomsgActivity activity from the toolbar
7- Open the hellomsgActivity property window and set it is properties as follows
a- FName:”Mohammed”
b-LName:”Atef”
Now Run the application you will find the following messages are printed
Hello Mohammed Atef”
Custom Activity using Designer: Hello Mr. Mohammed Atef”
Now we have finished this example and the main workflow1.xaml should be like the following picture below
You can also download the complete example by source code from here.
Back to other Workflow Foundation 4.0 posts
I hope that helped.
Workflow Foundation 4.0 Tracking
Introduction
This post will guide you to use extensions for tracking Workflow events while it is executing the defined activities.This is useful for monitoring a workflow’s execution and for triggering external processing.
Tracking
In WF 4.0, tracking is accomplished through tracking participants, which are extensions that are derived
from the TrackingParticipant abstract class.
First of all to implement tracking extensions you need to inherit TrackingParticipant class and overrides the Track() method, which is where most of the work is done. When a trackable event occurs, the workflow instance will enumerate all the extensions and will call the Track() method in any that are derived from the TrackingParticipant base class.
A TrackingRecord is passed into the Track() method. This is an abstract class; the actual instance passed
in will be one of the following classes, which are derived from the TrackingRecord class:
• WorkflowInstanceRecord contains data about the workflow instance.
You can use this class to print the Workflow instance ID and Workflow instance state as shown in the below list of codes:
1: WorkflowInstanceRecord instance = record as WorkflowInstanceRecord;
2: if (instance != null)
3: {
4: Console.WriteLine(String.Format(" InstanceID: {0} State: {1}",instance.InstanceId, instance.State));
5: }
• BookmarkResumptionRecord contains data about the bookmark being resumed.
You can use this class to print the book mark name as shown in the below list of codes:
1: BookmarkResumptionRecord bookmark = record as BookmarkResumptionRecord;
2: if (bookmark != null)
3: {
4: Console.WriteLine(String.Format(" Bookmark {0} resumed",bookmark.BookmarkName));
5: }
• ActivityStateRecord contains data about a specific activity.
You can use this class to print information about activities hosted in the Workflow instance as shown in the below list of codes:
1: ActivityStateRecord activity = record as ActivityStateRecord;
2: if (activity != null)
3: {
4: IDictionary<String, object> variables = activity.Variables;
5: StringBuilder s = new StringBuilder();
6: if (variables.Count > 0)
7: {
8: s.AppendLine(" Variables:");
9: foreach (KeyValuePair<string, object> v in variables)
10: {
11: s.AppendLine(String.Format(" {0} Value: [{1}]",
12: v.Key, v.Value));
13: }
14: }
15: Console.WriteLine(String.Format(" Activity: {0} State: {1} {2}",
16: activity.Activity.Name, activity.State, s.ToString()));
17: }
• CustomTrackingRecord contains user-defined data.
You can use this class to print about user-defined data as shown in the below list of codes
1: CustomTrackingRecord user = record as CustomTrackingRecord;
2: if ((user != null) && (user.Data.Count > 0))
3: {
4: Console.WriteLine(String.Format(" User Data: {0}", user.Name));
5: foreach (string data in user.Data.Keys)
6: {
7: Console.WriteLine(String.Format(" {0} : {1}", data, user.Data[data]));
8: }
9: }
To use the tracking extension you should add the tracking extension to the Workflow instance as shown in the below list of codes
1: i.Extensions.Add(_tracking);
2: //where i is an instance of WorkflowApplication
3: //and _tracking is an instance of class that inherits TrackingParticipant class
Then you should set up the tracking extensions participants by using TrackingProfile collections, A TrackingProfile defines a collection of queries that specify which events are to be tracked by the associated tracking participant. These queries are used to determine if an event is trackable. The queries are stored in the Queries property, which is a collection of classes derived from the abstract TrackingQuery class. There are four derived classes that correspond to the four types of tracking records:
• WorkflowInstanceQuery
A WorkflowInstanceQuery is used to define the workflow instance events that should be tracked. These
are the process states that occur at the instance level such as Started, Completed, Unloaded, and so on, To do that you can write the below list of codes:
1: TrackingProfile = new TrackingProfile()
2: {
3: Name = "TrackingProfileName",
4: Queries =
5: {
6: // For instance data, only track the started and completed events
7: new WorkflowInstanceQuery()
8: {
9: States = { WorkflowInstanceStates.Started,WorkflowInstanceStates.Completed },
10: }
11: }
• BookmarkResumptionQuery
In a BookmarkResumptionQuery, you specify the name of the bookmark that you want to track whenever it is resumed. You can specify only a single bookmark in a query. If you want to track multiple bookmarks,
you should create multiple queries—one for each bookmark, To do that you can write the below list of codes:
1: TrackingProfile = new TrackingProfile()
2: {
3: Name = "TrackingProfileName",
4: Queries =
5: {
6: new BookmarkResumptionQuery()
7: {
8: Name = "GetAssignment"
9: }
10: }
• ActivityStateQuery
An ActivityStateQuery class specifies both the Name of the activity and the State collection (events) that
should be tracked. You can specify an asterisk (*) for either, which indicates that all activities and/or
states should be tracked, To do that you can write the below list of codes:
1: TrackingProfile = new TrackingProfile()
2: {
3: Name = "ListBoxTrackingProfile",
4: Queries =
5: {
6: // For activity data, track all states of the InvokeMethod
7: new ActivityStateQuery()
8: {
9: ActivityName = "InvokeMethod",
10: States = { "*" },
11: }
12: }
13: }
• CustomTrackingQuery
The CustomTrackingQuery specifies the ActivityName, which indicates the activity that generated the
CustomTrackingRecord and the Name property, which indicates the name given to the CustomTrackingRecord,You can specify an asterisk for either (or both) as the example, above does. When
both are set to *, it indicates that all user events should be tracked, you can do that by typing the below of codes:
1: TrackingProfile = new TrackingProfile()
2: {
3: Name = "ListBoxTrackingProfile",
4: Queries =
5: {
6: new CustomTrackingQuery()
7: {
8: Name = "*",
9: ActivityName = "*"
10: }
11: }
12: }
In this post we have understood the Tracking extension and we have learned how to use it also.now you can visit the orher posts about Workflow Foundation for here
I hope that helped.
Workflow Foundation 4.0 Extensions
Introduction
Workflow foundation Extensions allow you to add configurable behavior to a workflow solution. The activities that you include in your workflow define the steps that are performed, while the extensions provide the operating environment that these activities are executed in, the persistence extension is not aware of what activities are executed; the extension however, provides the ability to persist those activities (whatever they might be) to a durable store.
SQL Persistence Extension
There are two key aspects of extensions that make then extremely useful. First, as was inferred
earlier, they are configurable. For example, the persistence provider that was used SqlWorkflowInstanceStore was designed to use a SQL Server database. Without changing the
application or the workflow definition.
The second aspect of extensions is that they can be accessed both from the application as well as the
workflow activities. This provides a convenient tool for sharing information between the application and
the workflow.
To use SQL Persistence you need to declare a reference to the InstanceStore class that will be used to persist and load the workflow instances :
1: private InstanceStore _instanceStore;
Then add you need to Loaded event handler and configures the store to do that you need to write the following code
1: _instanceStore = new SqlWorkflowInstanceStore(_connectionString);
2: InstanceView view = _instanceStore.Execute(_instanceStore.CreateInstanceHandle(),
3: new CreateWorkflowOwnerCommand(),TimeSpan.FromSeconds(30));
4: _instanceStore.DefaultInstanceOwner = view.InstanceOwner;
where the _connectionstring is the DB sql connection string used for your SQL Persistence.above code has an InstanceStore that is an abstract class from which all persistence providers are derived. An instance of the concrete class SqlWorkflowInstanceStore is created, passing the connection string in the constructor.
The parameters to the Execute() method are a handle (provided by InstanceStore ), a command, and a
timeout value. It returns an InstanceView class, which is roughly analogous to a connection handle.
Now you need to setup The Workflow persistence, to do that add the following codes listed below
1: i.InstanceStore = _instanceStore;//where i is and instance of WorkflowApplication
2: i.PersistableIdle = (waiea) => PersistableIdleAction.Unload;
3: i.Run();
The above code configures the workflow instance to be persisted. First, it sets the InstanceStore
property using the reference created as described early. It then provides an event handler for
the PersistableIdle event, which tells the instance to unload itself from memory. It is persisted to the
database prior to being unloaded.
Custom Extensions
here we will learn how to build a custom extension and how to use it in you Workflow. we are going to build simple custom extension that used to store the connection string, Instead of passing the connection string as an input argument, any activity that needs the connection string can access it from this extension.
Assume that we will create class called that hold the custom extension and implemented as shown in list below
1: using System;
2: public class ConStringExtension
3: {
4: private string _connectionString = "";
5:
6: public DBExtension(string connectionString)
7: {
8: _connectionString = connectionString;
9: }
10:
11: public string ConnectionString { get { return _connectionString; } }
12: }
The above code simply defines a private member that holds the connection string. The value of this string
is passed in the class constructor. A public property is provided for accessing this string.To add the new Custom extension to you Workflow instance add the following code
1: ConStringExtension _dbExtension = new ConStringExtension(_connectionString);
2: i.Extensions.Add(_dbExtension);
Finally to use this custom extension into code activity add the following line
1: ConStringExtension ext = context.GetExtension<ConStringExtension>();
I hope you have understand Workflow extensions well ,now you can check other posts about work flow here
I hope that helped.
-
Archives
- May 2011 (2)
- January 2011 (2)
- December 2010 (1)
- September 2010 (8)
- August 2010 (1)
- March 2010 (1)
- November 2009 (1)
- October 2009 (3)
- September 2009 (2)
- August 2009 (1)
- July 2009 (7)
- June 2009 (10)
-
Categories
-
RSS
Entries RSS
Comments RSS

