共用方式為


Windows Workflow + SharePoint 2003 + BizTalk Scenario Built Out

So I had a use case from a customer to build a sample using BizTalk Server, Windows Workflow Foundation, and Windows SharePoint Services 2003. It was
an interesting journey, and wanted to show how I got all these pieces to play nicely.

Before diving into each technical component, here's a summary of the use case.

  1. Purchase Order document received from ERP system.
  2. BizTalk looks at the document, assigns a reviewer, and publishes it to SharePoint.
  3. SharePoint kicks off a workflow which creates a Task item for that user, and potentially sends them an email.
  4. User loads up PO, and approves it.
  5. SharePoint again triggers workflow which then marks users Task as "complete."

Now as you may know, the next version of SharePoint (I think called SharePoint 2007) will have Windows Workflow integrated. But for now, it doesn't. So
there are multiple components that I put together to make this work.

Component: SharePoint Doc Library Configuration

First things first, I had to create the SharePoint document library. I did this by creating a BizTalk XSD schema, then creating an InfoPath form
that used it, and finally publishing that InfoPath form to my SharePoint site, thus generating the library with an associated template. Once I had
the library, I had to view the Change Advanced Settings page under Modify Columns and Settings. From here, I can define an event
handler which will fire whenever a change is made to the SharePoint library. So, once I have it built, I'll reference my Event Handler class here.

Component: SharePoint Event Handler

This one was fun. I created a class that referenced the Microsoft.SharePoint assembly and implemented the IListEventSink interface. It also
references the System.Workflow.Runtime assembly. The OnEvent method accepts a parameter of type SPListEvent. This is great since
that object contains all sorts of info about the document and library that spawned the event. My first decision in this method is to determine
which event just fired:

if (listEvent.Type == SPListEventType.Insert || listEvent.Type == SPListEventType.Update)

Then I created an instance of the WorkflowRuntime object that will execute the workflow. Since a workflow can accept any sort of parameter in,
I fill the input dictionary with the SharePoint event (SPListEvent) and a string indicating whether an Insert or Update has occurred.
Finally, I stop the runtime when the Workflow has completed. Now, after throwing the assembly in the GAC, I can go back to SharePoint and add my
Event Handler class.

Component: Windows Workflow

My workflow looks like this:

This class references the core SharePoint library again so that I can interact with the site. My first workflow shape simply initiates all my member
variables that are used by later steps. For instance, I have a member variable that is a pointer to my SharePoint site. I just took the SPListEvent
that came into my workflow and used its Site member to gain my reference:

activeSite = currentSPEvent.Site.OpenWeb();

Then I went about setting all sorts of variables for sending email, and so forth. One tricky thing to be aware of. So when I created my SharePoint document
library via InfoPath, I promoted a number of columns. Now, those values are available to me in the SPListEvent object. However, the names of the
columns don't come through, but rather, a GUID. That mapping between the name of the promoted column and the GUID identifier is stored in the SharePoint
library's Forms folder in the properties.xfp file. So, I just grabbed the GUID for the column I wanted and set my variable as such:

taskStatus = Convert.ToString(currentSPEvent.PropertiesAfter["xd_{5264C7A5-9142-488F-923B-0721E8770775}"]);

Got that?

The next shape that fires (assuming an Insert operation) is the CreateTask action. The code is fairly simple and looks like this:

SPListItemCollection listItems = activeSite.Lists[taskListName].Items;

SPListItem wfTask = listItems.Add();

//set task values

wfTask["ReqID"] = reqID;

wfTask["Title"] = taskTitle;

wfTask["Assigned To"] = activeSite.Users[@taskAssignment];

wfTask["Status"] = taskStatus;

wfTask["Start Date"] = DateTime.Now;

//insert task into collection

wfTask.Update();

For the SendMail shape, I simply utilize the System.Net.Mail assembly to build out a simple mail message. If an update is occuring, I need
to see if the status is Complete, and if so, mark the appropriate SharePoint task as Complete. So if the task is Complete I use the
SPQuery object to build a query that only returns the task with the corresponding ReqID value, then update that task.

//build query to pull back task with specific ReqID

SPQuery itemQuery = new SPQuery();

itemQuery.Query = "<Where><Eq><FieldRef Name='ReqID'></FieldRef><Value Type='Text'>" + reqID + "</Value></Eq></Where>";

SPList taskList = activeSite.Lists[taskListName];

SPListItemCollection taskListItems = taskList.GetItems(itemQuery);

//if task still exists ...

if (taskListItems.Count > 0)

{

//set status to completed

SPListItem wfTask = taskListItems[0];

wfTask["Status"] = "Completed";

try

{

wfTask.Update();

System.Diagnostics.EventLog.WriteEntry("WF", "Task Updated");

}

catch (Exception ex)

{

System.Diagnostics.EventLog.WriteEntry("WF", ex.ToString());

}

And that's pretty much the workflow piece.

Component: BizTalk Server

My BizTalk piece right now is fairly brain-dead. Just picks up a file, sends it to the SharePoint library, thus triggering the workflow.

Final solution

So what does this look like right now? A file gets put in my SharePoint library ...

Then, a task is created ...

Next an email is sent ...

User changes the doc, and saves it, and the Task gets updated to Complete ...

Gotchas

I glossed over a few things that were indeed comically tricky. So, here are a few notes, and links that helped me out ...

  • SharePoint site that hosts the document library must be running under ASP.NET 2.0
  • Only a single workflow runtime can be loaded in the App Domain at one time. So, I'll have modify my process for starting workflow instances
    in order to handle parallel load.
  • Had to incorporate some impersonation in order to get the Workflow the necessary permissions to interact with SharePoint. Used the
    WindowsImpersonationContext object and during variable initialization in the Workflow I started the impersonation, and at the end of the
    process I executed the Undo() method to reset permissions on the thread.
  • Absolutely invaluable SharePoint SDK for the full SharePoint API.
  • MSDN article demonstrating some SharePoint eventing
  • Part 1 and Part 2 of useful blog series
  • Nice, but a bit outdated Windows Workflow article on MSDN.
  • Helpful post on SharePoint events and .NET 2.0

Comments

  • Anonymous
    February 19, 2006
    Very interesting and definitely useful.  We are currently looking at blending WSS, WWF and BT into our environment.  It's my responsibility to do the planning, programming, rollout, etc. for the entire project.  But, this stuff intrigues me and seems very straightforward.  We're just wondering if we need to configure BT any special way to what we need.  
  • Anonymous
    February 19, 2006
    Hey Robert,  if you are using Sharepoint or BizTalk to call a Windows Workflow, you'll have to think about how to host the workflow.  That is, if you're doing anything but single-threaded processing, you'll probably want to use a WF service to manage the WF host and provide instances.
  • Anonymous
    February 21, 2006
    The comment has been removed
  • Anonymous
    February 21, 2006
    Thanks Kevin for the comments and insight.  Great call on integrating with ContextMenus.  Nice idea.
  • Anonymous
    March 09, 2006
    This is a great example for building a simple workflow using WinWF.  Something to consider though is WinWF's applicability to meet the needs of the Enterprise.  You will find that using WinWF for multi-stage serial and parallel processes becomes a very cumbersome and ardous task due to the managability and code maintainence involved in rolling out Enterprise level business process automation.  Be mindful of when you extend an irrational escaltion of commitment to a framework, when you really need a manageable enterise ready platform for building, deploying and maintaining complex business workflows.  

    You can spend most of your time and resources managing business processes and achieving business results (which is a task in and of itself for most organizations) or spend three times the amount of time and resources managing the code, architectures and the processes associated with complex workflow scenarios.  

    When you have internalized this awareness, feel free to reach out for K2.net - an Enterprise ready BPM platform geared for Microsoft technologies.  We take WinWF to the next level, to meet the needs of today's Enterprises.

    http://www.k2workflow.com
  • Anonymous
    March 09, 2006
    Don't worry Mark, for "real" human workflow, I still recommend you guys!  Since your next release sits on WF, it's good for folks to know the base framework anyway.
  • Anonymous
    March 19, 2006


    Very very nice article in this month's MSDN Magazine that goes into great depth explaining how to...
  • Anonymous
    May 22, 2006
    It's a nice example but It could be better with code and componentes in a .zip file (like MSDN articles). I'll appreciate if you attach these files.

    Thanks.

    Leo
  • Anonymous
    May 22, 2006
    The comment has been removed