|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Want a new Job?
Chapters
Services
Feature Zones
|
IntroductionThe article is aimed at learning how the Windows Workflow Foundation (WF) can be implemented in a real-time scenario. Before proceeding further, I would like to state that it is designed in such a way to give your Workflow skills a better insight before really jumping in to the real world and developing enterprise-wide applications. It is basically to attain an Intermediate level in understanding the Windows Workflow Foundation, targeting to develop an Issue Management application. Before jumping in to such an exciting stuff, I would recommend you go through the basics of the Windows Workflow Foundation from the “n” number of sites that are available. Once you get some basic understanding of the architecture of the Workflow Foundation and may be an understanding of the development of a “Hello World” sample, you are good to go for developing a scaled-down version of an application before gaining full-fledged expertise on developing an end to end enterprise wide application. I would like to touch base on the fundamental understanding of the Windows Workflow Foundation before moving further. Windows Workflow Foundation (WF)The Windows Workflow Foundation (WF) is nothing but a set of activities. WF has to run within a process, which could be a SharePoint portal server, or a Windows application, or a Console application, or an ASP.NET application. Without a host process, a Workflow is nothing. There are two types of Windows Workflows available.
In this demo application, I am going to use a Statemachine Workflow. Just start visualizing on how an Issue management kind of application would fit in to a Statemachine Workflow before we hit to the real implementation of it. Windows Workflow Foundation (WF) is basically a layered architecture, with the top layer being the base activity library providing out-of-box activities that enable developers to write the business logic/flow of a process by using conditional loops, web service calling etc. It also offers to construct activities. The runtime engine is responsible for executing a workflow, responding to various events, State Management, Persistence of Workflow, tracking, and threading to decide whether a Workflow runs on separate threads etc. This not only supports the base activity library, but also provides other accessibility points which are the Runtime services. Runtime Services are a set of services which are responsible for persisting the state of a Workflow (out-of-the-box, we have the SQL Server Persistence provider; custom persistence services such as DB2, XML structure etc.) communicating between the workflow application and the tracking service, and also for defining how to do the tracking of information as things are executed within a Workflow. It also provides Scheduling Services and Transaction Services. Let's Dive into the ImplementationWith the above mentioned fundamentals, let’s start developing an Issue Management application using the various activities that are shipped with the base activity library of the Windows Workflow Foundation (WF) by leveraging the Statemachine Workflow. The various activities that I am going to use are:
I am going to exclude the Persistence Services and Tracking Services, but the application would behave as if the Persistence Services are enabled. Please note that for enabling Persistence Services (SQL Server persistence services), you got to do some ground work such as creating a database for storing the state of the Workflow and retrieving it at a later point of time. Please note that the application that I am going to show is a scaled down version. All the components (such as Workflow, Host, and Services) are bundled in a single application, which you might want to structure into various layers as you plan to create a scaled up version of the same application, and this is what is recommended from a best practices stand-point. The way I am developing the application might not be the best and might not adhere to the standards. Requirements of the SystemEvery organization would typically have an issue management system. An application where issues are logged at the helpdesk, and then assigned to concerned persons for resolving, and later they are marked as either Fixed, Cancelled (if it is invalid), or Deferred. Once it is fixed, it will be sent to a QA team who will test/retest the issue before closing it. If the test results are found unsatisfactory, they would have an option to Re-open it, and it will be sent to the Process activity for fixing. Here, we have added an Escalation flow as well, where if an issue is cancelled, it will be escalated to that department manager, where he/she would analyze and then it can be marked as closed or it can be reopened to send it across to the Process activity by the manager. Development of the ApplicationWith that requirement set, let’s put that in the Windows Workflow Foundation - Statemachine terms. For building such an application on WF, all we need is a Workflow defining the various activities such as New Issue Logging, Processing, Retesting, Escalating, and Terminating once closed. Let’s first design the Workflow diagram by dragging the various activities. The figure below illustrates the end to end workflow of the Issue management application. Let me give you a brief overview:
Within the various states ( Once a particular event is fired through the Event driven activity, we would like to perform some business related operation, for which we use the Nevertheless, for every business operation, we need to go back and forth the parameters for the business logic to deal with. In this demo application, we need to pass various parameters such as Issue log data, Issue Title, Issue description etc for the business logic to make use of it and to run through certain business rules. This would be accomplished by defining a class which should be derived from the So far, I have explained the flow involved in this operation and how those can be accomplished by using various activities. However, the activities are not limited to what I have explained above; there are many other activities available in the Model layer of the Windows Workflow Foundation (WF) which can be used as per our requirements. If required, custom activities can also be developed. Alright, let's switch gears to define the linking between these activities for making it an end to end Workflow. As I have told earlier, we need to pass parameters for the business logic to deal with, so let’s go ahead and create a parameter which is of the [Serializable]
public class Issue
{
private DateTime dateReported;
private string title;
private string description;
private string status;
public DateTime DateReported
{
get { return dateReported; }
set { dateReported = value; }
}
public string Title
{
get { return title; }
set { title = value; }
}
public string Description
{
get { return description; }
set { description = value; }
}
public string Status
{
get { return status; }
set { status = value; }
}
}
Make sure that the “ Let me now show you how to configure In order to use this activity, two properties should be set which are Interface type and Event name. The Interface that can be set for the Interface type property should be marked with Let’s take a slight diversion here, and create an Interface and events in it. Following is the Interface that I created. Name of the file is IIssueManagement.cs. [ExternalDataExchangeAttribute]
public interface IIssueManagement
{
event EventHandler<issueeventargs> OnNewIssueCreate;
event EventHandler<issueeventargs> OnNewIssueFixed;
event EventHandler<issueeventargs> OnNewIssueCancelled;
event EventHandler<issueeventargs> OnFixedReOpen;
event EventHandler<issueeventargs> OnFixedClose;
event EventHandler<issueeventargs> OnManagerReOpen;
event EventHandler<issueeventargs> OnManagerClose;
void NewIssueUpdate(Issue newIssue);
void NewIssueFixed(Issue newIssue);
void NewIssueCancelled(Issue newIssue);
void FixedReopen(Issue newIssue);
void FixedClose(Issue newIssue);
void ManagerReopen(Issue newIssue);
void ManagerClose(Issue newIssue);
}
The Interface is attributed with [Serializable]
public class IssueEventArgs : ExternalDataEventArgs
{
public IssueEventArgs(Guid workflowId, Issue issue): base(workflowId)
{
this.issueToProcess = issue;
this.workflowId = workflowId;
}
private Guid workflowId;
private Issue issueToProcess;
public Guid WorkflowId
{
get { return workflowId; }
set { workflowId = value; }
}
public Issue IssueToProcess
{
get { return issueToProcess; }
set { issueToProcess = value; }
}
}
Note that this class is derived from Let’s go back to the As we know that public IssueEventArgs issueEventArgs = default(IssueEventArgs);
Now, go to the properties of Now, I want to apply some business rules on the supplied void NewIssueFixed(Issue newIssue);
Since we have used an “ You need to perform these steps for all the other external Event driven activities such as I have used As the Interface ( public void NewIssueUpdate(Issue issue)
{
Console.WriteLine("New Issue has been received.");
Console.WriteLine("Issue Current Status: " + issue.Status);
Console.Write("Enter the Status <fixed>: ");
}
public void NewIssueFixed(Issue issue)
{
Console.WriteLine("New Issue has been Fixed.");
Console.WriteLine("Issue Current Status: " + issue.Status);
Console.Write("Enter the Status <reopen>: ");
}
public void NewIssueCancelled(Issue issue)
{
Console.WriteLine(
"Issue has been Cancelled. It has been escalated to Manager.");
Console.WriteLine("Issue Current Status: " + issue.Status);
Console.WriteLine("Enter the Option <managerreopen>: ");
}
public void FixedReopen(Issue issue)
{
Console.WriteLine("Fixed Issue has been ReOpened.");
Console.WriteLine("Issue Current Status: " + issue.Status);
Console.Write("Enter the Status <fixed>: ");
}
public void FixedClose(Issue issue)
{
Console.WriteLine("Issue has been CLOSED.");
Console.WriteLine("Issue Current Status: " + issue.Status);
}
public void ManagerReopen(Issue issue)
{
Console.WriteLine("Manager has ReOpened the Issue.");
Console.WriteLine("Issue Current Status: " + issue.Status);
Console.Write("Enter the Status <fixed>: ");
}
public void ManagerClose(Issue issue)
{
Console.WriteLine("Issue has been escalated to Manager and it is CLOSED.");
Console.WriteLine("Issue Current Status: " + issue.Status);
}
I will create various other methods that would be called from the host application for invoking an event ( public Guid NewIssueCreate(Issue newIssue)
{
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("Issue Received with Status: " + newIssue.Status);
waitHandle = new AutoResetEvent(false);
workflowRuntime = new WorkflowRuntime();
ExternalDataExchangeService dataService =
new ExternalDataExchangeService();
workflowRuntime.AddService(dataService);
IssueManagementService issueMgmtService =
IssueManagementService.IMInstance;
dataService.AddService(issueMgmtService);
workflowRuntime.WorkflowCompleted +=
new EventHandler<workflowcompletedeventargs>(
workflowRuntime_WorkflowCompleted);
workflowRuntime.StartRuntime();
WorkflowInstance workflowInstance =
workflowRuntime.CreateWorkflow(typeof(WorkflowIssueTrack));
workflowInstance.Start();
bool eventResult = RaiseEvent(OnNewIssueCreate,
newIssue, workflowInstance.InstanceId);
if (eventResult == false)
{
Console.WriteLine("Invalid Status is entered.");
}
return workflowInstance.InstanceId;
}
Please note that, first we need to start the Workflow instance and we need to raise the event that is desired. For other methods shown below, we will not start the Workflow instance as it will be running behind the scenes in this particular sample. However, in a real time application, the Workflow instance should be started for every operation before raising the desired event. Below is the code for various methods that will be called based on the status of the public void NewIssueFix(Issue newIssue, Guid workflowId)
{
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("Issue Received with Status: " + newIssue.Status);
bool eventResult = RaiseEvent(OnNewIssueFixed, newIssue, workflowId);
if (eventResult == false)
{
Console.WriteLine("Invalid Status is entered.");
}
}
public void NewIssueCancel(Issue newIssue, Guid workflowId)
{
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("Issue Received with Status: " + newIssue.Status);
bool eventResult = RaiseEvent(OnNewIssueCancelled, newIssue, workflowId);
if (eventResult == false)
{
Console.WriteLine("Invalid Status is entered.");
}
}
public void IssueReopen(Issue newIssue, Guid workflowId)
{
Console.ForegroundColor = ConsoleColor.DarkYellow;
Console.WriteLine("Issue Received with Status: " + newIssue.Status);
bool eventResult = RaiseEvent(OnFixedReOpen, newIssue, workflowId);
if (eventResult == false)
{
Console.WriteLine("Invalid Status is entered.");
}
}
public void ManagerReopen(Issue newIssue, Guid workflowId)
{
Console.ForegroundColor = ConsoleColor.White;
Console.WriteLine("Issue Received with Status: " + newIssue.Status);
bool eventResult = RaiseEvent(OnManagerReOpen, newIssue, workflowId);
if (eventResult == false)
{
Console.WriteLine("Invalid Status is entered.");
}
}
public void ManagerClose(Issue newIssue, Guid workflowId)
{
Console.ForegroundColor = ConsoleColor.DarkGray;
Console.WriteLine("Issue Received with Status: " + newIssue.Status);
bool eventResult = RaiseEvent(OnManagerClose, newIssue, workflowId);
if (eventResult == false)
{
Console.WriteLine("Invalid Status is entered.");
}
}
public void NewIssueClose(Issue newIssue, Guid workflowId)
{
Console.ForegroundColor = ConsoleColor.Gray;
Console.WriteLine("Issue Received with Status: " + newIssue.Status);
bool eventResult = RaiseEvent(OnFixedClose, newIssue, workflowId);
if (eventResult == false)
{
Console.WriteLine("Invalid Status is entered.");
}
}
We will be calling the methods of this class from an Instance property (which returns an object of the class). First, I will call the Console.WriteLine("[Workflow is Started with New Issue]");
Console.WriteLine();
Guid workflowInstanceId =
IssueManagementService.IMInstance.NewIssueCreate(issue);
//Console.WriteLine("Issued to Workflow. Workflow ID: " +
workflowInstanceId.ToString());
string readInput;
do
{
readInput = Console.ReadLine();
issue.Status = readInput;
if (readInput.ToLower() == "fixed")
{
IssueManagementService.IMInstance.NewIssueFix(issue,
workflowInstanceId);
}
else if (readInput.ToLower() == "cancelled")
{
IssueManagementService.IMInstance.NewIssueCancel(issue,
workflowInstanceId);
}
else if (readInput.ToLower() == "reopen")
{
IssueManagementService.IMInstance.IssueReopen(issue,
workflowInstanceId);
}
else if (readInput.ToLower() == "close")
{
IssueManagementService.IMInstance.NewIssueClose(issue,
workflowInstanceId);
}
else if (readInput.ToLower() == "managerreopen")
{
IssueManagementService.IMInstance.ManagerReopen(issue,
workflowInstanceId);
}
else if (readInput.ToLower() == "managerclose")
{
IssueManagementService.IMInstance.ManagerClose(issue,
workflowInstanceId);
}
} while (IssueManagementService.watchProcess == "Running");
OutputLet’s see the final output which covers all the activities. Let’s try to supply an option that is not expected after a state; the Workflow should throw an exception. Below is the output when trying to “ You can see the text in red color (an exception thrown) which was the result of calling the wrong activity. Later “ Also, you can introduce a new flow from one of the ConclusionPlease note that, this application is developed assuming a single thread of Workflow running during the complete life cycle of the
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||