DOC

blogsvertigocom

By Kim Thompson,2014-04-28 00:34
8 views 0
blogsvertigocom

    Simple Mortgage Application Workflow

    This document will walk through at a high level, how we can use Windows Workflow Foundation to implement a simple User business process.

    We’ll assume a pretty simple Business Process. In this case we are going to model a Mortgage loan

    Application process. At a very high level, here are the main steps:

    ; Application is received Either via Snail Mail(in

    which case it is scanned in) or as an on-line

    submission

    o If we received the application via Mail,

    then we need to view the scanned

    images and enter the index information

    ; Send an acknowledgement email to the

    applicant

    ; Inspect the Loan amount. If we have a loan

    amount > $1 million then we will give a higher

    level of service, so we place it in the “Gold Q”,

    otherwise it goes into the “Bronze Q”

    ; Employees open pending applications from

    their respective Queue’s and make a decision

    on the application

    ; If the Application is rejected, then send an

    email notification to the applicant and Archive

    ; If the Application is Approved, then we send

    the work to the Loan Origination workflow

    essentially a new Workflow

WF High Level Architecture

    The Windows Workflow Architecture is depicted below. The Workflow Runtime is the component that

    allows invocation and interaction with all the other workflow sub components. There is obviously the Business Process we build, which will most likely use some custom activities

    remember that as a Foundation, we don’t get a whole stack of activities, rather a neat framework that

    allows us to create our own.

    WF also has the concept of services that provide extended functionality, such as tracking and

    persistence which I will go over later on.

    The Workflow Host is where our custom applications do their part They are the day to day Web

    Applications, Windows Smart Apps, Mobile apps or Web Services etc.

    Workflow Host

    Workflow Host

    Workflow Host

    Workflow Runtime

    Workflow Instance

    Persistence ServiceTracking Service

    Custom ActivitiesProcess

    StartActivities

    ActivitiesProcess

    Activities?

    ProcessProcess

    Persistence EndTracking DataStore

Workflow Hosts

    For our mortgage application process we have identified the following Host Applications:

    1. Receive Applications.

    This is actually two host applications per the requirements; we need a Web Application and a

    Scan Application

    2. Index Application.

    If the application is received via Snail Mail, then we need to index it

    3. Process Application.

    Once the applications have been received and indexed as needed, we will look at them and

    make our decision to grant a loan

    Automated Host = Custom Activity

    One of the requirements call out for some automated processing, sending an email response to the submitter. This could easily be added in to the Host Applications, but it is also a perfect candidate for creating a generic Custom Activity that can be re-used as necessary.

    The Workflow Process

    Our first task is to submit an application so let’s see what this entails. Remember that WF is a FOUNDATION. There is no built in support for creating documents etc., so we have to build our own. Using the WF metaphors for workflow, we need to create a Workflow Instance, which is nothing more than a .Net Assembly, but using the Workflow Assemblies (System.Workflow).

    The Workflow Instance is just like any other application we create. It has properties/function etc. So we just need to create a workflow instance that contains our necessary data. After several days of tweaking and playing around I came up with a very simple framework that allowed me to have a somewhat generic workflow.

    The key to the kingdom

    The premise was based on having a piece of work, or a Workitem, which was exposed to the host and

    contained all the required methods and properties needed to allow it’s routing through the workflow.

    Here are the basics of the Workitem object:

Workitem also inherits from a WorkItemBase which exposes the following properties:

The Attachments property is further broken down as follows:

The Attributes property is simply a collection of Name Value Pairs

    Using the above model allows us to add documents / files etc. as attachments and set attribute information as required. This really is the basis for the entire framework we use.

Custom Activities

    Before we can start to build our workflow, we need to implement a few custom activities. These are steps that are executed as part of the workflow and are the main extensibility point we have in WF. We need 3 activities for this workflow as follows:

    1. Application

    2. Send Email

    3. Save to Queue

    Application Activity

    A workflow instance runs completely independently of its host. It could be on the same machine, or it could be on a remote machine etc. so we have no direct means of communicating with our workflow instance. WF gives us the ability to “pause” the workflow by putting it on a queue and awaiting a response to reactivate it. This is how we communicate with our Workflow instance, and also how we set properties on the Workflow instance.

    There are many tutorials already to show how we create an activity, but the important parts we need for our activity are as follows:

    protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext) { WorkflowQueuingService queueService = executionContext.GetService<WorkflowQueuingService>(); WorkflowQueue currentQueue = queueService.CreateWorkflowQueue(this.WorkflowInstanceId.ToString(), true); currentQueue.QueueItemAvailable += new EventHandler<QueueEventArgs>(currentQueue_QueueItemAvailable); return ActivityExecutionStatus.Executing; }

    The Execute function of our Activity, creates a new queue, and then creates a delegate for the QueueItemAvailable event, which is where we handle the return from the host. By returning a value of ActivityExecutionStatus.Executing we have told WF that this Workflow instance is still running. Below is the code for handling the item being returned. When we queue the item from our host, we supply data we can abstract here. In this case, we are going to use our pre-defined WorkItem.

    void currentQueue_QueueItemAvailable(object sender, QueueEventArgs e) { ActivityExecutionContext executionContext = sender as ActivityExecutionContext; WorkflowQueuingService queueService = executionContext.GetService<WorkflowQueuingService>(); WorkflowQueue currentQueue = queueService.GetWorkflowQueue(e.QueueName); this.WorkItem = (WorkItem)currentQueue.Dequeue(); TrackData(this.WorkItem); queueService.DeleteWorkflowQueue(e.QueueName); executionContext.CloseActivity(); }

    We retrieve the item from the queue, get its new WorkItem value, then call the CloseActivity()

    function, which tells WF that we are ready to resume the Workflow.

    The only other noteworthy item in here is how we declare our WorkItem property. This is a “shared”

    property that we want to reuse in other activities, and in evaluations on the workflow etc. To that end we make it a DependencyProperty as follows:

    public static DependencyProperty WorkItemProperty;

    We also need to register this property using the following:

    static Application() { WorkItemProperty = DependencyProperty.Register("WorkItem", typeof(WorkItem), typeof(Application)); }

    Finally our setter/getter is defined as follows:

    public Vertigo.WFRuntime.WorkItem WorkItem { get

     { return (Vertigo.WFRuntime.WorkItem)GetValue(WorkItemProperty); } set { SetValue(WorkItemProperty, value); } }

    Send Email Activity

    This will be a pretty simple task to send out emails. Nothing special here, we simply create our email using the public properties we have defined and send it using the normal methods. protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext) { SmtpClient smtpClient = new SmtpClient(); smtpClient.Host = this.SMTPHostName; smtpClient.Credentials = new NetworkCredential(this.SMTPHostUserName, this.SMTPHostPassword); smtpClient.UseDefaultCredentials = false; MailMessage mailMessage = new MailMessage(); mailMessage.IsBodyHtml = this.MessageIsHTML; mailMessage.Subject = this.Subject; mailMessage.Body = this.Message; mailMessage.From = new MailAddress(this.FromAddress); mailMessage.To.Add(new MailAddress(this.ToAddress)); if ((this.CCAddress != string.Empty) && (this.CCAddress != null)) { mailMessage.CC.Add(new MailAddress(this.CCAddress)); } if ((this.BCCAddress != string.Empty) && (this.BCCAddress != null)) { mailMessage.Bcc.Add(new MailAddress(this.BCCAddress)); } smtpClient.Send(mailMessage); return ActivityExecutionStatus.Closed; }

    Note that in this case we are returning ActivityExecutionStatus.Closed which tells the Workflow

    Instance to continue its processing.

Save to Queue Activity

    What we want to do when this activity executes is persist the Workflow Instance, allowing us to reactivate it at a later time. We are assuming the use of the SqlWorkflowPersistenceService in

    this case and in reality its implementation is to serialize the Workflow Instance and place it into a single table in a SQL Server Table. We are going to use a combination of Tracking and Persistence to specify a Queue Name. Essentially we just set an Attribute on our Workflow Instance WorkItem ,

    CurrentQueue, and we are then able to use tracking to search for items with a specified value. In order to have WF persist the Workflow Instance, we simply decorate our Activity class with the [PersistOnClose] attribute as follows:

    [PersistOnClose]

public class SaveToQueue : Activity

    Then our Execute function couldn’t be any simpler:

protected override ActivityExecutionStatus Execute(ActivityExecutionContext executionContext) { this.WorkItem.CurrentQueue = CurrentQueue; TrackData(this.WorkItem); return ActivityExecutionStatus.Closed; }

    We have public property, CurrentQueue, which we map to the Tracked WorkItem. Returning ActivityExecutionStatus.Closed along with the [PersistOnClose] attribute tells the Workflow Runtime to persist the current Instance.

Building the Workflow

    Now we have all of our building blocks done in the Activities, we can start to build our workflow. We

    start by creating a new Sequential Workflow Library.

    Add in references to the Custom Activities we created. Note that these all need to be strongly named

    and in the GAC.

    The first thing we need to do is create a reference to the WorkItem property in our workflow, so go in to the Code View for the Wokflow and ass the following:

     public sealed partial class MortgageApplication: SequentialWorkflowActivity

     public static DependencyProperty WorkItemProperty = DependencyProperty.Register("WorkItem", typeof(WorkItem), typeof(MortgageApplication)); [DesignerSerializationVisibilityAttribute(DesignerSerializationVisibility.Visible)] [BrowsableAttribute(true)] [CategoryAttribute("Parameters")] public WorkItem WorkItem { get { return ((WorkItem)(base.GetValue(MortgageApplication.WorkItemProperty))); } set { base.SetValue(MortgageApplication.WorkItemProperty, value); } }

This definition now means that we have one shared instance of the WorkItem property that we can

    reference throughout the workflow route.

Lets now switch to the design surface and add our first activity, the will be the Application

    Activity that allows our host to submit new applications. In the properties window we see our

    DependencyPropery, WorkItem highlighted by the small blue information icon, click in the value field and an ellipse will appear click this to allow us to bind the WorkItem for the workflow to our activity.

You will see the WorkItem public property we just added to the Workflow.

    In our solution, we have two applications that will be submitting new WorkItems (creating Workflow

    Instances. A Web Application and a Scan Application. We now need a way to detect where the work came. So using the Attributes on the WorkItem we create, we can set a property to indicate this.

    You could obviously use anything, but for my purposed I used the property name Channel and set it to

    Internet or Mail

    So let’s add the decision in to our workflow. Drag on a IfElse activity. Select the left branch and in the

    properties tab, drop down the Condition to say Declarative Rule Condition. A small + will

    appear in front of Condition, expand, enter a Condition name, eg. InternetChannel and select the Expression ellipse to display the Rule Condition Editor. Here we can specify our condition for this branch of the condition. The really cool thing about this editor is that it has full Intellisense built in! So our

expression would be something like:

So right here you see the flexibility of having our framework and the WorkItem construct. We can now

    set any attributes we need from our host and evaluate decisions on their values in the workflow! This is VERY powerful stuff.

    From here on in the workflow is pretty much straight forward. Drag on the appropriate Activities and map the WorkItem property as required.

    So it should now be easy to see how we can branch based on a loan value. We set an attribute from our host, “LoanValue” or something similar, and then create another Declarative Rule using the above

    syntax, but with our new attribute name:

WorkItem specifics

    Now we have our workflow and activities all set, let’s delve a little deeper in the WorkItem

    implementation. It’s simply amazing that from having so few methods and a simple attribute mechanism that we can build very powerful systems.

    The WorkItem is simply a wrapper for generic functions that we need to do using the System.Workflow.Runtime and System.Workflow.Runtime.WorkflowInstance objects. The

    main functions are described below:

    Create()

    The create method simply creates a new instance of a Workflow and its implementation is very straight

    forward:

    wFInstance = workflowRunTime.CreateWorkflow(workflowType); ; wFInstance.Load(); wFInstance.Start();

    Early on I mentioned that the Workflow is merely an assembly that uses the System.Workflow

    assemblies. Once compiled it should be added to the GAC. The workflowType above is simply a Type

    of the workflow you wish to create

    Forward()

    Once we have created a WorkItem, manipulated its properties / attributes / attachments etc. we forward the work to have the workflow continue its process:

    wFInstance.EnqueueItem(wFInstance.InstanceId.ToString(), this, null, null);

    In this one line of code we put the the WorkItem back into its queue, along with the current Attributes

    and Attatchment values the this parameter acheives this for us.

    SearchQueue(string queueName)

    If we want to search for all WorkItems in a certain queue, we just use Tracking to do a search on the

    CurrentQueue attribute for the WorkItem. This function returns a list of all the matching WorkItems, which we can select from using their ID:

    SqlTrackingQuery sqlTrackingQuery = new SqlTrackingQuery(connStr); SqlTrackingQueryOptions sqlTrackingQueryOptions = new SqlTrackingQueryOptions(); List<WorkItemBase> searchresults = new List<WorkItemBase>(); foreach (SqlTrackingWorkflowInstance sqlTrackingWorkflowInstance in sqlTrackingQuery.GetWorkflows(sqlTrackingQueryOptions)) { if (sqlTrackingWorkflowInstance.UserEvents.Count != 0) { WorkItem lastSearchItem = null; foreach (UserTrackingRecord userTrackingRecord in sqlTrackingWorkflowInstance.UserEvents) { lastSearchItem = (WorkItem)userTrackingRecord.UserData; if (QueueName != string.Empty) { if (QueueName != lastSearchItem.CurrentQueue) {

Report this document

For any questions or suggestions please email
cust-service@docsford.com