Using the AJAX Workflow Monitor With .NET 3.5 Beta 2

Awhile back, Jon Flanders posted an excellent example of creating an AJAX enabled Workflow Monitor using ASP.NET AJAX 1.0.  What's really cool is that you can very easily upgrade this sample to use with .NET 3.5 Beta 2 and use it without downloading the ASP.NET AJAX libraries since all of the ASP.NET AJAX goodness is rolled into the framework. 

Using Windows Workflow Foundation, you can enable tracking for your application to gain visibility into your application's processing lifecycle.  Just like you would add an entry in your config to persist the workflow, you also add entries in config to enable tracking.

           <workflowRuntime name="WorkflowServiceHostRuntime" validateOnCreate="true" enablePerformanceCounters="true">            
            <services>
              <add type="System.Workflow.Runtime.Hosting.SqlWorkflowPersistenceService, System.Workflow.Runtime, Version=3.0.00000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                   connectionString="Server=(local);Database=SqlPersistenceService;Integrated Security=True"
                   UnLoadOnIdle="true"  />
              <add type="System.Workflow.Runtime.Tracking.SqlTrackingService, System.Workflow.Runtime, Version=3.0.00000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
                   connectionString="Server=(local);Database=Tracking;Integrated Security=True"/>

            </services>
          </workflowRuntime>

When you enable the tracking service, WF will automatically write entries into the tracking database that you specified in the config section.  The AJAX Workflow Monitor sample then uses the APIs in the .NET Framework to query the tracking store and provide the UI rendering that you see in the screen shot below.  The image you see is dynamically generated.  This sample allows you to see the workflow instances as well as the ActivityExecutionStatus for each of its contained activities.  For instance, here is a screen shot that shows a workflow that I am working on that uses the new ReceiveActivity in .NET 3.5.

 ASP.NET AJAX Workflow Monitor

There are some subtleties in the picture that you might not notice.  The first is that there are checkmarks next to each of the activities that have already completed (in this case, all of them are checked).  There is also an activity called "CreateTicket" that is highlighted, indicating the status that matches what I clicked in the list on the right.  If you have wanted to use WF in your projects but just couldn't seem to convince your boss or teammates, this is one of those demos that gets everyone excited about WF.  You can gain real visibility into your projects and see how your complex business logic is functioning.  Just run your workflow and then pull up the Workflow Monitor to show where the workflow is at.

To use this with .NET 3.5 Beta 2, simply download the source and open the solution in Visual Studio 2008 Beta 2.  In the web site AtlasWebSite1, upgrade your web.config to the following:

 <?xml version="1.0"?>
<configuration>
  <configSections>
    <sectionGroup name="system.web.extensions" type="System.Web.Configuration.SystemWebExtensionsSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
      <sectionGroup name="scripting" type="System.Web.Configuration.ScriptingSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
        <section name="scriptResourceHandler" type="System.Web.Configuration.ScriptingScriptResourceHandlerSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
        <sectionGroup name="webServices" type="System.Web.Configuration.ScriptingWebServicesSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
          <section name="jsonSerialization" type="System.Web.Configuration.ScriptingJsonSerializationSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="Everywhere"/>
          <section name="profileService" type="System.Web.Configuration.ScriptingProfileServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
          <section name="authenticationService" type="System.Web.Configuration.ScriptingAuthenticationServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
          <section name="roleService" type="System.Web.Configuration.ScriptingRoleServiceSection, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" requirePermission="false" allowDefinition="MachineToApplication"/>
        </sectionGroup>
      </sectionGroup>
    </sectionGroup>
  </configSections>
  <appSettings/>
  <connectionStrings>
    <add connectionString="server=.;database=Tracking;trusted_connection=yes" name="WFTracking"/>
  </connectionStrings>
  <system.web>
    <compilation debug="true">
      <assemblies>
        <add assembly="System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
        <add assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
        <add assembly="System.Data.DataSetExtensions, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
        <add assembly="System.Xml.Linq, Version=3.5.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
        <add assembly="System.Workflow.Activities, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
        <add assembly="System.Workflow.ComponentModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
        <add assembly="System.Workflow.Runtime, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
      </assemblies>      
    </compilation>
    <authentication mode="Windows"/>
    <pages>
      <controls>
        <add namespace="WorkflowVisibilityControl" assembly="WorkflowVisibilityControl" tagPrefix="wf"/>
        <add namespace="WorkflowAtlasDesigner" assembly="__code" tagPrefix="wfa"/>

        <add tagPrefix="asp" namespace="System.Web.UI" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
        <add tagPrefix="asp" namespace="System.Web.UI.WebControls" assembly="System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
      </controls>
    </pages>
    <httpHandlers>
      <remove verb="*" path="*.asmx"/>
      <add verb="*" path="*.asmx" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
      <add verb="*" path="*_AppService.axd" validate="false" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
      <add verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" validate="false"/>
    </httpHandlers>
    <httpModules>
      <add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
    </httpModules>
  </system.web>
  <system.codedom>
    <compilers>
      <compiler language="c#;cs;csharp" extension=".cs" compilerOptions="/warnaserror-" warningLevel="4" type="Microsoft.CSharp.CSharpCodeProvider, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
        <providerOption name="CompilerVersion" value="v3.5"/>
      </compiler>
      <compiler language="vb;vbs;visualbasic;vbscript" extension=".vb" compilerOptions="/optioninfer+" type="Microsoft.VisualBasic.VBCodeProvider, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
        <providerOption name="CompilerVersion" value="v3.5"/>
      </compiler>
    </compilers>
  </system.codedom>
  <system.webServer>
    <validation validateIntegratedModeConfiguration="false"/>
    <modules>
      <add name="ScriptModule" preCondition="integratedMode" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
    </modules>
    <handlers>
      <remove name="WebServiceHandlerFactory-Integrated"/>
      <add name="ScriptHandlerFactory" verb="*" path="*.asmx" preCondition="integratedMode" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
      <add name="ScriptHandlerFactoryAppServices" verb="*" path="*_AppService.axd" preCondition="integratedMode" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
      <add name="ScriptResource" preCondition="integratedMode" verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
    </handlers>
  </system.webServer>  
</configuration>

Note that you might need to update the connection strings for each of the services for your environment.  I could have trimmed the config down some (removing references to LINQ since it's not used, getting rid of the asmx extension handling since there's no ASMX in this project), that's an exercise left to the reader.

There's one other subtlety to this example that you shoudl realize.   The monitor needs to find your assembly in order to render the workflow image.  That means that you will either need to GAC the workflow assembly or deploy it to the web site's bin directory.  For simplicity in debugging, I did the latter, adding a post build step to my workflow.

 copy "$(TargetDir)*.*" "C:\Users\kirke\Documents\Visual Studio 2008\Projects\AtlasWorkflowMonitor\AtlasWebSite1\Bin\"

Once the workflow monitor web project can "see" your assembly, it will be able to query the tracking store and render dynamic images of your workflow.