Collaborative Workflow
Collaborative Workflow
Figure 1 The Visual Studio 2010 SharePoint Project Templates SharePoint Designer 2010 also has a new graphical workflow designer for designing workflows and deploying them directly to SharePoint. It can also be used as a prototyping tool before workflows are further developed in Visual Studio 2010. Another improvement in SharePoint Designer 2010 is that it now allows you to edit the out-of-the-box workflows that come with SharePoint. It will also use InfoPath forms if InfoPath is installed or the Data Form Web Part if InfoPath is not installed; previously, only ASPX forms were used by SharePoint Designer 2007. Since you can use InfoPath forms now, it makes it very easy to edit those forms later with InfoPath to improve them. If you have new columns specified in a workflow, they will automatically be added to the schema of the list that you associate with the workflow. These are known as association columns and can be used throughout the SharePoint Designer workflow. User profile data can be bound to properties in workflow, making it possible to get information about the SharePoint user profile. For example, you could look up the name of a direct manager for approvals. SharePoint Designer 2010 includes a new task process designer that creates an activity in the workflow but allows flexibility in how you manage the approval process all within that one activity. This includes defining escalation processes, delegation, defining approval requirements and semantics of what happens if the underlying document is changed during the approval. Workflow models can be created in Visio 2010 for further editing in SharePoint Designer 2010. Visio 2010 supports Business Process Modeling Notation. It also supports sub-processes and containers to break up the diagram and validation to analyze a diagram to ensure it is properly constructed.
item or document. This is such a common scenario that developers will create dummy lists and items in order to just to add workflows. In Visual Studio 2010, you simply pick Site Workflow when creating the workflow project item, as shown in Figure 2.
Figure 2 The Site/List Workflow Dialog Page You can get to the activated site workflows by choosing Site Workflows from the Site Actions menu in SharePoint. It shows you new workflows you can start, any running site workflows and completed workflows. Since site workflows dont have a list item or document to start from, they must be started manually through the SharePoint user interface or via the SharePoint API. Reusable Declarative Workflows In SharePoint Designer 2010, you can create reusable declarative workflows. In SharePoint 2007, a workflow model created in SharePoint Designer could only be associated with one list. These reusable workflows do not rely on a specific list but can be associated with any list. The workflow design can be reused and associated with multiple lists. Also, the Save as Template command can be used to create a WSP file containing a reusable workflow which can be moved to another SharePoint server or to Visual Studio 2010. In SharePoint Designer, this means you dont have access to the list fields to access data since no specific list structure is connected.
Figure 3 The SharePoint Designer Workflow Showing a High Privilege Impersonation Step SPTimer Location SharePoint executes workflow instances in one of two places depending on the last action. If the last action in the workflow was waiting on a user input, the workflow continues to execute on the Web front end where the user completed that input. If the workflow is continued from a delay timer or from an event being received elsewhere, it executes within the SPTimer service. Using a new option in SharePoint Central Administration, you can now set the preferred server where the SPTimer service runs. To do this, click in the Manage Content Databases menu of the Application Management section of SharePoint Central Administration. Then click on your content database and scroll down to the setting for Preferred Server for Timer Jobs (as shown in Figure 4). You can also manually stop the SPTimer service on any servers you dont want it to run on.
Figure 5 The Event Receivers That Can Be Built in Visual Studio 2010 SharePoint 2007 made it easy to involve people in workflows, but it was difficult to send and receive messages with external systems. The recommended approach was to use task items to send a message to the external system using a Web service and have the external system update the task to return the result. SharePoint 2010 adds support for pluggable workflow services. These will be familiar to Windows Workflow Foundation (WF) developers and are defined by creating an interface containing methods and events. This interface is connected to a pair of activities called CallExternalMethod and HandleExternalEvent. A tool comes with WF in the .NET Framework called WCA.exe to generate strongly-typed activities for sending and receiving based on the interface. These generated activities can be used in SharePoint workflow also. They do not require the interface to be set as properties and instead can be dragged directly from the toolbox to the workflow design surface. The service code that integrates with SharePoint workflow is loaded into the GAC for access by the workflow. It needs to inherit from the SPWorkflowService base class and it needs to be referenced in the web.config. Using these new activities, you can send asynchronous messages to an external system right from within the SharePoint workflow. I will investigate creating and using these activities later in this article in the second walkthrough.
Creating a new SharePoint workflow project is easy. Choose sequential workflow on the new project template selector as shown previously in Figure 1. This can also be done with the state machine workflow style. The new workflow project wizard has four pages. The first is a page that all SharePoint tools project templates have in common, where you identify the URL of the local SharePoint site you want to use to deploy and debug your solution, as shown in Figure 6.
Figure 6 First Page of the New Workflow Project Wizard The second is a page where you supply the name of the workflow and choose whether to associate it with a list or as a site. This page is shown earlier in Figure 2. The third page is where you decide if Visual Studio will automatically associate the workflow for you or if you can do it manually after it has been deployed. If you choose to leave the box checked, it has selections for the list to associate with (if you previously chose a list-based workflow), the workflow history list to use and the task list to use (see Figure 7). Typically, the history and task list will not need changing.
Figure 7 Third Page of the New Workflow Project Wizard The fourth and final page is where you select how the workflow can be started (see Figure 8). You should not unselect all three of these or your workflow will be very difficult to start. For a site workflow, you can only choose manually starting the workflow. For a list-based workflow, you can also choose to start the workflow instance when a document is created or to start when a document is changed.
Figure 8 Fourth Page of the New Workflow Project Wizard The new blank workflow model looks like Figure 9 in the design surface in Visual Studio 2010. The workflow activated activity is derived from the HandleExternalEvent activity and provides initialization data from SharePoint to the workflow instance. I will cover more of the HandleExternalEvent activity later.
Figure 9 The Default Blank Workflow Showing the WorkflowActivated Activity Step 2 Add an initiation form to the workflow An initiation form can be shown to the user when they start the workflow. It allows the workflow to gather parameters before it gets started. This can be added in Visual Studio 2010 easily by right clicking on the workflow item in Solution Explorer and choosing Add then New Item, as shown inFigure 10. Select the Workflow Initiation Form template and the new form is automatically associated with the workflow. It is an ASPX form that you edit in HTML (see Figure 11).
Figure 10 Adding a Workflow Initiation Form One method called GetInitiationData needs editing in the code behind the ASPX file. This method only returns a string; if there are multiple values, it is recommended you serialize them into an XML fragment before returning them. Once the workflow instance is running, it is easy to get to this string just by referencing workflowProperties.InitiationData. The multiple values will need to be de-serialized from the XML fragment if they were serialized in GetInitiationData.
For the initiation form, a single text field will be added and then that field will be returned from the GetInitiationData method. The tags in the following code are added inside the first asp:Content tag, like so:
<asp:TextBox ID="MyID" runat="server" /> <br />
The GetInitiationData method already exists and just needs to have code added to return the MyID.Text property. The following code shows the updated method code:
private string GetInitiationData() { // TODO: Return a string that contains the initiation data that will be passed to the workflow. Typically , this is in XML format. return MyID.Text; }
Step 3 Add a workflow log activity The LogToHistoryList activity is extremely easy to use. Each workflow instance created has a history list that can display in the SharePoint user interface. The activity takes a single string parameter and adds an item to that list. It can be used for reporting the status of workflow instances to users in production. Simply drag the LogToHistoryList activity from the toolbox to the workflow design and set the description property (see Figure 12).
Figure 12 The Visual Studio Toolbox Showing SharePoint Workflow Activities Properties in workflows are called dependency properties. These are bound at runtime to another activities dependency property, such as a field, a property or a method. This process is often called wiring up and it is what allows activities to work together in a workflow even though they dont have specific type information for each other at compile time. Each property in the property window in Visual Studio is wired to a class field or class property in the workflow class. Fields are the simplest to create and the dialog that comes up when wiring up the workflow property allows for creating new fields, creating new properties or wiring to existing ones.
Figure 13 Dependency Property Binding to a Created Field Figure 13 shows adding a new field with the default name. The following shows the added code to the MethodInvoking event handler for the activity to set the property to the workflow initiation data:
You can actually set the HistoryDescription property directly in code since this property doesnt connect between activities, but it is a good simple activity in order to learn a little about dependency properties. Step 4 Add a CreateTask activity This next step is the main part of human workflow interaction known as the SharePoint task item. Create a task, assign it to a person and then wait for the person to make changes to that task. The CreateTask activity has to be dragged onto the workflow design surface and then configured with all the required properties. Figure 14 shows the CreateTask properties window just after the activity is dragged on to it.
Figure 14 The Properties Pane Showing the CreateTask Activity The first thing needed here is a correlation token for the task. A correlation token is used for message correlation in workflow. It provides a unique identifier that enables a mapping between a task object in a specific workflow instance and the workflow runtime in SharePoint. This is used so that when SharePoint receives a message for the workflow instance it can locate the correct task within the correct workflow instance. The CorrelationToken property must be configured and its not recommended to use the WorkflowToken for tasks, although this isnt prevented in the tool. Enter the new name for the correlation token as TaskToken and press enter. Then expand the (+) symbol which appears and click the drop down to the right of OwnerActivityName and choose the workflow. The TaskId must be configured and a new GUID specified for the task id. This is done by selecting the TaskId property and clicking the [] ellipsis to bring up the property editor. Click the Bind to a new member tab, choose Create Field, and then click OK. The same must be done for the TaskProperties property by again selecting the property, clicking on the ellipsis and adding a new field. Next, double click on the new CreateTask activity on the workflow design surface to bring up the createTask1_MethodInvoking handler code and set the properties in code. The new task must be given a title, and for good measure I will set the task description to the string I got from the initiation form. Once all those properties have been set, this is the added code:
public Guid createTask1_TaskId1 = default(System.Guid); public SPWorkflowTaskProperties createTask1_TaskProperties1 = new Microsoft.SharePoint.Workflow.SPWorkflowTaskProperties();
Step 5 Add the OnTaskChanged and CompleteTask activities Use the While activity to wait for multiple changes to the task until you see the changes you want. The While activity must contain another activity, such as the OnTaskChanged activity. The Listen activity can also be used to listen for multiple events at one time by adding a Listen activity with actual event receiver activities inside the Listen branches. Alternatively, you can just wait until the task is deleted with the OnTaskDeleted activity. A common requirement is to escalate or timeout waiting for an event. This is done by using a Listen activity to listen for both the task changed message and a timer message by using a Delay activity. The first message to be received by either contained activity resumes the workflow and the other activity stops waiting. The Delay activity is sent a message when the timeout occurs. There are lots of options described above but the simplest is to use OnTaskChanged which will wait for any change to the task item and then continue on. To configure the OnTaskChanged, you need to wire up the CorrelationToken and the TaskId properties to the same fields as the CreateTask. Last, add a CompleteTask activity and again set the CorrelationToken and TaskId properties. As shown in Figure 15, the activities that wait for a message are green while activities that send a message are blue. This is consistent throughout SharePoint workflow.
Figure 15 The Completed Simple Task Workflow Model Step 6 Deploy and Test the Workflow
Now the workflow is almost complete, so press F5 and wait for the deployment. Pressing F5 will compile the workflow, package the workflow into a WSP, deploy that WSP to SharePoint, activate the SharePoint features, attach the Visual Studio 2010 debugger to SharePoint and start Internet Explorer with the SharePoint site. Once the SharePoint site appears, choose Site Workflows from the Site Actions menu. You will see all of the site workflows and you can click on yours to start it. Once it is selected, you will see your workflow initiation form, as shown in Figure 16.
Figure 16 The Workflow Initiation Form Other improvements can be added to this basic starter workflow model, such as using a content type to define the task you assign to a person, using email to notify the user they have been assigned a task, and even using InfoPath forms to create more complex forms for users to complete in email or online.
received. The correlation token is used to ensure the correct workflow instance is resumed when a response is received. As an example, consider two common examples of long-running work. One example is a synchronous Web service request where the caller is blocked until a response is received on the channel. This is not suited to run within a workflow activity. The workflow service needs to wait for the Web service call to complete and then to send a message back to the workflow instance with the response. Another example is a CPU intensive work item that needs to run but because it may take longer than a second it needs to run outside of the workflow activity. Again, you can use a workflow service and create a thread in the workflow service to execute the CPU intensive work item. The new thread can send a message back once it is finished. Both of these examples are implemented with the same pattern. The following walkthrough shows how to create a new SharePoint Sequential Workflow and call a pluggable workflow service that factors prime numbers to identify how many prime numbers there are under 100,000,000. This work takes too long to execute within the main line of an activity. Step 1 Create a Pluggable Workflow Service To create a pluggable workflow service in Visual Studio 2010, you need to implement the service as an interface and class in your project. Figure 17 shows an example of a pluggable workflow service which can be added to your project in a new class file called MyService.cs. In the code, the interface definition is in IMyService, the interface implementation in class MyService and the MessageOut method runs most of the logic through an anonymous method delegate on a separate thread. In the separate thread, the call to RaiseEvent will send the message back to the waiting HandleExternalMessage activity. Figure 17 The Pluggable Workflow Service
// Interface declaration [ExternalDataExchange] public interface IMyService { event EventHandler<MyEventArgs> MessageIn; void MessageOut(string msg); }
// Arguments for event handler [Serializable] public class MyEventArgs : ExternalDataEventArgs { public MyEventArgs(Guid id) : base(id) { } public string sAnswer; }
// Class for state class FactoringState { public SPWeb web; public Guid instanceId; public FactoringState(Guid instanceId, SPWeb web) { this.instanceId = instanceId; this.web = web; } }
// Interface implementation class MyService : Microsoft.SharePoint.Workflow.SPWorkflowExternalDataExchangeService, IMyService { public event EventHandler<MyEventArgs> MessageIn; public void MessageOut(string msg) { ThreadPool.QueueUserWorkItem(delegate(object state) { FactoringState factState = state as FactoringState; DateTime start = DateTime.Now; int topNumber = 100000000; BitArray numbers = new System.Collections.BitArray(topNumber, true);
for (int i = 2; i < topNumber; i++) { if (numbers[i]) { for (int j = i * 2; j < topNumber; j += i) numbers[j] = false; } } int primes = 0; for (int i = 2; i < topNumber; i++)
{ if (numbers[i]) primes++; }
string sAnswer = "Found " + primes + " in " + Math.Round(DateTime.Now.Subtract(start).TotalSeconds, 0) + " seconds";
// Send event back through CallEventHandler RaiseEvent(factState.web, factState.instanceId, typeof(IMyService), "MessageIn", new object[] { sAnswer }); }, new FactoringState(WorkflowEnvironment.WorkflowInstanceId, this.CurrentWorkflow.ParentWeb)); }
// Plumbing that routes the event handler public override void CallEventHandler(Type eventType, string eventName, object[] eventData, SPWorkflow workflow, string identity, System.Workflow.Runtime.IPendingWork workHandler, object workItem) { var msg = new MyEventArgs(workflow.InstanceId); msg.sAnswer = eventData[0].ToString(); msg.WorkHandler = workHandler; msg.WorkItem = workItem; msg.Identity = identity; // If more than one event - you'd need to switch based on parameters this.MessageIn(null, msg); } public override void CreateSubscription(MessageEventSubscription subscription) { throw new NotImplementedException(); } public override void DeleteSubscription(Guid subscriptionId) { throw new NotImplementedException(); }
Once the pluggable workflow service is constructed, it needs to be registered with the workflow runtime that runs in SharePoint. This is done by editing the web.config section for workflow services. The web.config editing required should be done using a feature receiver that calls SPWebApplication.WebConfigModifications.Add. The following code shows the additional configuration entry to be added:
<WorkflowServices><WorkflowService Assembly="WorkflowProject1, Version=1.0.0.0, Culture=neutral, PublicKeyToken=YOURPUBLICKEY" Class="WorkflowProject1.MyService"> </WorkflowService></WorkflowServices>
The assembly name and public key information can be obtained using GACUTIL.EXE /l. Step 3 Create the workflow model The rest of the work is easy. Add Call External Method and Handle External Event activities to the workflow and configure them to point to this service, as shown in Figure 18. The Call External Method activity will call MessageOut in the service and the Handle External Event activity will wait for the event MessageIn. This involves setting the InterfaceType and EventName properties in each of the two activities.
Figure 19 Completed Prime Calculating Pluggable Workflow Service Step 4 Run the workflow When this workflow is run, it will start a separate thread in the pluggable workflow service to do the prime number calculations. After 10-15 seconds of running, a workflow history item appears as shown in Figure 19. It shows that there are 5,761,455 prime numbers under 100,000,000. As shown, there is some code to write for pluggable workflow services and more code to write for each system you connect to. There are two options for reducing the amount of code required to communicate with external services. One is by interfacing with BizTalk Server and making use of the BizTalk adapter library for connecting other systems. The other is using the business connectivity services in SharePoint 2010 which provide a way to expose data from another system through external lists.