ASP.NET And a State Machine Workflow
- I recently read the MSDN magazine article, Windows Workflow Foundation, Part 2 (http://msdn.microsoft.com/msdnmag/issues/06/04/CuttingEdge/), where Dino Esposito demonstrates how to work with workflows in ASP.NET. Dinos' demonstration provides a helpful solution to working with Sequential workflows; however, it left me a little confused with how to work with a State Machine workflow.
In the article, Dino demonstrates how to pass parameters to a Sequential workflow by using a Dictionary object, which is then passed as a parameter to the CreateWorkflow() method. The problem is that State Machine workflows pass parameters through event arguments.
Using the OrderWorkflows (beta 2, lab 4) example as a starting point, is it possible to use the methods in the OrderLocalService class to raise the events?
Thank you in advance...
Answers
You will need to have some of the files from the Hands on Labs, which can be found at http://www.microsoft.com/downloads/details.aspx?FamilyId=5DF74E3B-FB51-4A94-A11D-DFF70288A8BB&displaylang=en, to put this together.
- Create a new Web Site project.
- Add a new Empty Workflow Project to the solution.
- Add the empty workflow project as a reference to the web site.
- Add IOrderService.cs and OrderService.cs from the hands on lab 4, that can be found under Resources\OrderLocalServices, to the workflow project.
- Add Workflow1.cs also from the hands on lab 4, that can be found under Completed\Exercise 2\OrderWorkflows\OrderWorkflows, to the workflow project.
- Replace the content for the default Default.aspx with the following:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="Default2" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Order Service Sample</title>
</head>
<body>
<form id="form2" runat="server">
<div>
<br />
Order Number
<asp:TextBox ID="txtOrderNumber" runat="server" TabIndex="1">12345</asp:TextBox><br />
<br />
Workflow InstanceId -
<asp:Label ID="lblWorkflowInstanceId" runat="server" Width="462px" ForeColor="#00C000"></asp:Label><br />
<br />
Workflow Status -
<asp:Label ID="lblOrderStatus" runat="server" Width="245px" ForeColor="Blue"></asp:Label><br />
<br />
<asp:Button ID="btnCreateOrder" runat="server" Text="Create Order" OnClick="btn_Click" TabIndex="2" Enabled="False" />
<asp:Button ID="btnProcessOrder" runat="server" OnClick="btn_Click" TabIndex="3"
Text="Process Order" Enabled="False" />
<asp:Button ID="btnShipOrder" runat="server" OnClick="btn_Click" Text="Ship Order" Enabled="False" /></div>
</form>
</body>
</html>
7. Replace the content for the default Default.aspx.cs with the following:
using System;
using System.Web.UI.WebControls;
using System.Workflow.Runtime;
using OrderLocalServices;
using System.Workflow.Runtime.Hosting;
using System.Workflow.Activities;
using System.Web;
public partial class Default2 : System.Web.UI.Page
{
private void StartWorkflow()
{
WorkflowRuntime workflowRuntime = Application["WorkflowRuntime"] as WorkflowRuntime;
// Now get a reference to the ManualWorkflowSchedulerService
ManualWorkflowSchedulerService scheduler =
workflowRuntime.GetService<ManualWorkflowSchedulerService>();
ExternalDataExchangeService dataService = workflowRuntime.GetService<ExternalDataExchangeService>();
OrderLocalServices.OrderService orderService = workflowRuntime.GetService<OrderLocalServices.OrderService>();
if (orderService == null)
{
orderService = new OrderLocalServices.OrderService();
dataService.AddService(orderService);
}
//// Attach to the WorkflowCompleted event
workflowRuntime.WorkflowCompleted += new EventHandler<WorkflowCompletedEventArgs>(WorkflowRuntime_WorkflowCompleted);
WorkflowInstance workflowInstance = workflowRuntime.CreateWorkflow(typeof(OrderWorkflows.Workflow1));
workflowInstance.Start();
// Now run the workflow. This is necessary when
// ...using the ManualWorkflowSchedulerService
scheduler.RunWorkflow(workflowInstance.InstanceId);
this.lblWorkflowInstanceId.Text = workflowInstance.InstanceId.ToString();
this.lblOrderStatus.Text = GetCurrentState(workflowInstance.InstanceId);
}
private string GetCurrentState(Guid instanceId)
{
StateMachineWorkflowInstance stateInstance = new StateMachineWorkflowInstance(Application["WorkflowRuntime"] as WorkflowRuntime, instanceId);
return stateInstance.CurrentStateName;
}
void WorkflowRuntime_WorkflowCompleted(object sender, System.Workflow.Runtime.WorkflowCompletedEventArgs e)
{
HttpContext.Current.Response.Redirect(string.Format("OrderCompleted.aspx?OrderNumber={0}&InstanceID={1}", txtOrderNumber.Text, lblWorkflowInstanceId.Text));
}
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack )
{
this.StartWorkflow();
this.btnCreateOrder.Enabled = true;
}
}
protected void btn_Click(object sender, EventArgs e)
{
Button currentButton = sender as Button;
Guid instanceId = new Guid(this.lblWorkflowInstanceId.Text);
OrderService orderService = (Application["WorkflowRuntime"] as WorkflowRuntime).GetService<OrderService>();
ManualWorkflowSchedulerService scheduler = (Application["WorkflowRuntime"] as WorkflowRuntime).GetService<ManualWorkflowSchedulerService>();
switch (currentButton.ID)
{
case "btnCreateOrder":
currentButton.Enabled = false;
orderService.RaiseOrderCreatedEvent(txtOrderNumber.Text, instanceId);
scheduler.RunWorkflow(instanceId);
lblOrderStatus.Text = GetCurrentState(instanceId);
this.btnProcessOrder.Enabled = true;
break;
case "btnProcessOrder":
currentButton.Enabled = false;
orderService.RaiseOrderProcessedEvent(txtOrderNumber.Text, instanceId);
scheduler.RunWorkflow(instanceId);
lblOrderStatus.Text = GetCurrentState(instanceId);
this.btnShipOrder.Enabled = true;
break;
case "btnShipOrder":
currentButton.Enabled = false;
orderService.RaiseOrderShippedEvent(txtOrderNumber.Text, instanceId);
scheduler.RunWorkflow(instanceId);
break;
}
}
}
8. Add a new web page named OrderCompleted.aspx to the web project.
9. Replace the content of the .aspx page with the following:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="OrderCompleted.aspx.cs" Inherits="OrderCompleted" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Order Completed</title>
</head>
<body>
<form id="form1" runat="server">
<div>
Order Completed<br />
<br />
<asp:Button ID="btnNewOrder" runat="server" OnClick="btnNewOrder_Click" Text="New Order" /></div>
</form>
</body>
</html>
10. Replace the content of the .aspx.cs page with the following:
using System;
using System.Web;
public partial class OrderCompleted : System.Web.UI.Page
{
protected void btnNewOrder_Click(object sender, EventArgs e)
{
HttpContext.Current.Response.Redirect("Default.aspx");
}
protected void Page_Load(object sender, EventArgs e)
{
lblOrderNumber.Text = HttpContext.Current.Request.QueryString["OrderNumber"];
lblWorkflowInstanceId.Text = HttpContext.Current.Request.QueryString["InstanceID"];
}
}
11. Add a Web.Config file to the web site and change the content to the following:
<?xml version="1.0"?>
<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<configSections>
<section name="WorkflowRuntime" type="System.Workflow.Runtime.Configuration.WorkflowRuntimeSection, System.Workflow.Runtime, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</configSections>
<appSettings/>
<connectionStrings/>
<system.web>
<authentication mode="Windows"/>
<httpModules>
<add type="System.Workflow.Runtime.Hosting.WorkflowWebHostingModule, System.Workflow.Runtime, Version=3.0.00000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" name="WorkflowHost"/>
</httpModules>
<compilation debug="true">
<assemblies>
<add assembly="System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
<add assembly="System.Drawing.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
<add assembly="System.Transactions, Version=2.0.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"/>
<add assembly="Microsoft.Build.Tasks, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
<add assembly="System.Messaging, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
<add assembly="System.Runtime.Remoting, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
<add assembly="System.DirectoryServices, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
<add assembly="System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
<add assembly="Microsoft.Build.Utilities, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
<add assembly="Microsoft.Build.Framework, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
</assemblies>
</compilation>
</system.web>
<WorkflowRuntime Name="WorkflowServiceContainer">
<Services>
<add type="System.Workflow.Runtime.Hosting.ManualWorkflowSchedulerService, System.Workflow.Runtime, Version=3.0.00000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<add type="System.Workflow.Activities.ExternalDataExchangeService, System.Workflow.Activities, Version=3.0.00000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</Services>
</WorkflowRuntime>
</configuration>
- Add a Global.asax file to the web site and update the Application_Start and End to the following:
void Application_Start(object sender, EventArgs e)
{
// NOTE: This requires the configuration section to be named "WorkflowRuntime".
System.Workflow.Runtime.WorkflowRuntime workflowRuntime = new System.Workflow.Runtime.WorkflowRuntime("WorkflowRuntime");
Application["WorkflowRuntime"] = workflowRuntime;
workflowRuntime.StartRuntime();
}
void Application_End(object sender, EventArgs e)
{
System.Workflow.Runtime.WorkflowRuntime workflowRuntime = Application["WorkflowRuntime"] as System.Workflow.Runtime.WorkflowRuntime;
workflowRuntime.StopRuntime();
}
You should now be able to build and run the project.
On default you can enter and order number, a default is provided. When the page is loaded the workflow is started. Click the enabled button and the updated state will be displayed. When the workflow completes you are taken to the order completed page and given a chance to create another order.
Hope this helps you to better understand how to get this working. Let me know if you have any questions.
You can find the competed sample here.
- Create a new Web Site project.
Yes, WorkflowWebRequestContext has been removed. Now you need to create and store the runtime on you own, as an Application or Session object.
UPDATED
I modified the sample to remove WorkflowWebRequestContext. Now the WorkflowRuntime is created in Application_Start and stored in the Application state.
- Take a look at the sample at http://blogs.msdn.com/tomlake/archive/2006/05/17/examples-of-using-persistence-and-tracking-in-asp-net.aspx.
All Replies
Hi.
I don´t fully understand your final question, this is my best guess.
In the lab example inside : private void btnOrderCreated_Click(object sender, EventArgs e) method there is a method inside firing the event :// Raise an OrderCreated event using the Order Local Service
_OrderService.RaiseOrderCreatedEvent(strOrderId, WorkflowInstanceId);Here you are raising the event, the state machine handles this event and then passes the state to the defined one inside the state machine.
Then the example becomes more complex, but here:switch (strButtonName)
{
case "btnOrderShipped":
// Raise an OrderShipped event using the Order Local Service
_OrderService.RaiseOrderShippedEvent(strOrderId, WorkflowInstanceId);
break;
case "btnOrderUpdated":
// Raise an OrderUpdated event using the Order Local Service
_OrderService.RaiseOrderUpdatedEvent(strOrderId, WorkflowInstanceId);
break;case "btnOrderCanceled":
// Raise an OrderCanceled event using the Order Local Service
_OrderService.RaiseOrderCanceledEvent(strOrderId, WorkflowInstanceId);
break;
case "btnOrderProcessed":
// Raise an OrderProcessed event using the Order Local Service
_OrderService.RaiseOrderProcessedEvent(strOrderId, WorkflowInstanceId);
break;
}shows how each event is fired.
Hope this helps a little.
Marcos.- Thank you for the response.
I understood the lab; but it wasn't clear to me how to make it work in ASP.NET. Below is essentially the code I'm using (diced up a bit, so hopefully it still compiles cleanly).
private void StartWorkflowRuntime()
{
Type t;
WorkflowInstance instance;
OrderLocalServices.OrderService LocalServices = new OrderLocalServices.OrderService();
WorkflowRuntime wr = WorkflowWebRequestContext.Current.WorkflowRuntime;
wr.WorkflowStarted += new EventHandler<WorkflowEventArgs>(wr_WorkflowStarted);
wr.WorkflowCompleted += new EventHandler<WorkflowCompletedEventArgs>(wr_WorkflowCompleted);
wr.WorkflowTerminated += new EventHandler<WorkflowTerminatedEventArgs>(wr_WorkflowTerminated);
wr.AddService(LocalServices);
t = typeof(OrderWorkflows.Workflow1);
instance = wr.CreateWorkflow(t);
instance.Start();
LocalServices.RaiseOrderCreatedEvent(strOrderId,
instance.InstanceId);
}
I set a break point on the RaiseOrderCreatedEvent() method. When I step through the code, it checks to make sure that the OrderCreated event is not null. In my case, this evaluates false, so the event is never raised. This is obviously an indication that there are no subscribers to the event; however, the Workflow DOES subscribe to the event in the OrderCreated state.
My guess is that I'm passing the wrong GUID for the Workflow to the RaiseOrderCreatedEvent() method, which results in the Runtime being unable to locate a Workflow with the ID I pass.
The winform application executes the workflow as expected. I'm just having trouble implementing it in ASP.NET
Thank you again... Can you try adding the following two lines after instance.Start and before LocalServices.RaiseOrderCreatedEvent:
ManualWorkflowSchedulerService scheduler = wr.GetService<ManualWorkflowSchedulerService>();
scheduler.RunWorkflow(instance.InstanceId);
Also, take a look at the ASP.NET Workflow example that can be found at http://www.windowsworkflow.net/Downloads/Examples/ASPNETWorkflowExample%20-%202006-01-14.exe
- Nope, that didn't do it. scheduler is null after the call to wr.GetService()
I have a suspicion on what's causing it. I'll give it a try and get back.
Thank you for the response... Make sure you have the following in your web.config file:
<WorkflowRuntime Name="WorkflowServiceContainer">
<Services>
<add type="System.Workflow.Runtime.Hosting.ManualWorkflowSchedulerService, System.Workflow.Runtime, Version=3.0.00000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<add type="System.Workflow.Runtime.Hosting.DefaultWorkflowTransactionService, System.Workflow.Runtime, Version=3.0.00000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</Services>
</WorkflowRuntime>
- That fixed the null reference problem, but it's still as if I have no subscribers to the event (although, like I said earlier, the Workflow does subscribe to the OrderCreated event and it works fine in my winform application).
I haven't touched the original workflow since the tutorial. Is there anything that I need to do to it to make it work with ASP.NET? I've looked at a few tutorials (like the one you posted), but I can't figure out what I'm not doing right. - The sample ASP.NET application in the article by Dino exposed an Authenticate() method as a web service. Will I need to take a similar approach for the OrderWorkflow lab to make it work in ASP.NET?
The intentions of the web service were unclear to me in the article. Don't feel like the lone ranger -- I have exactly the same problem -- no event subscribers. My worlfows are all dressed up with nobody to talk to

- Are you at least able to start a new instance of the runtime? In my case, instance.InstanceId appears to have a valid GUID representing the instance ID for the Workflow, which I assume means that the Workflow object was successfully created. As far as I can tell, the only thing that isn't working for me is the raising of events (which pretty much leaves my workflow useless
). You will need to have some of the files from the Hands on Labs, which can be found at http://www.microsoft.com/downloads/details.aspx?FamilyId=5DF74E3B-FB51-4A94-A11D-DFF70288A8BB&displaylang=en, to put this together.
- Create a new Web Site project.
- Add a new Empty Workflow Project to the solution.
- Add the empty workflow project as a reference to the web site.
- Add IOrderService.cs and OrderService.cs from the hands on lab 4, that can be found under Resources\OrderLocalServices, to the workflow project.
- Add Workflow1.cs also from the hands on lab 4, that can be found under Completed\Exercise 2\OrderWorkflows\OrderWorkflows, to the workflow project.
- Replace the content for the default Default.aspx with the following:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="Default2" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Order Service Sample</title>
</head>
<body>
<form id="form2" runat="server">
<div>
<br />
Order Number
<asp:TextBox ID="txtOrderNumber" runat="server" TabIndex="1">12345</asp:TextBox><br />
<br />
Workflow InstanceId -
<asp:Label ID="lblWorkflowInstanceId" runat="server" Width="462px" ForeColor="#00C000"></asp:Label><br />
<br />
Workflow Status -
<asp:Label ID="lblOrderStatus" runat="server" Width="245px" ForeColor="Blue"></asp:Label><br />
<br />
<asp:Button ID="btnCreateOrder" runat="server" Text="Create Order" OnClick="btn_Click" TabIndex="2" Enabled="False" />
<asp:Button ID="btnProcessOrder" runat="server" OnClick="btn_Click" TabIndex="3"
Text="Process Order" Enabled="False" />
<asp:Button ID="btnShipOrder" runat="server" OnClick="btn_Click" Text="Ship Order" Enabled="False" /></div>
</form>
</body>
</html>
7. Replace the content for the default Default.aspx.cs with the following:
using System;
using System.Web.UI.WebControls;
using System.Workflow.Runtime;
using OrderLocalServices;
using System.Workflow.Runtime.Hosting;
using System.Workflow.Activities;
using System.Web;
public partial class Default2 : System.Web.UI.Page
{
private void StartWorkflow()
{
WorkflowRuntime workflowRuntime = Application["WorkflowRuntime"] as WorkflowRuntime;
// Now get a reference to the ManualWorkflowSchedulerService
ManualWorkflowSchedulerService scheduler =
workflowRuntime.GetService<ManualWorkflowSchedulerService>();
ExternalDataExchangeService dataService = workflowRuntime.GetService<ExternalDataExchangeService>();
OrderLocalServices.OrderService orderService = workflowRuntime.GetService<OrderLocalServices.OrderService>();
if (orderService == null)
{
orderService = new OrderLocalServices.OrderService();
dataService.AddService(orderService);
}
//// Attach to the WorkflowCompleted event
workflowRuntime.WorkflowCompleted += new EventHandler<WorkflowCompletedEventArgs>(WorkflowRuntime_WorkflowCompleted);
WorkflowInstance workflowInstance = workflowRuntime.CreateWorkflow(typeof(OrderWorkflows.Workflow1));
workflowInstance.Start();
// Now run the workflow. This is necessary when
// ...using the ManualWorkflowSchedulerService
scheduler.RunWorkflow(workflowInstance.InstanceId);
this.lblWorkflowInstanceId.Text = workflowInstance.InstanceId.ToString();
this.lblOrderStatus.Text = GetCurrentState(workflowInstance.InstanceId);
}
private string GetCurrentState(Guid instanceId)
{
StateMachineWorkflowInstance stateInstance = new StateMachineWorkflowInstance(Application["WorkflowRuntime"] as WorkflowRuntime, instanceId);
return stateInstance.CurrentStateName;
}
void WorkflowRuntime_WorkflowCompleted(object sender, System.Workflow.Runtime.WorkflowCompletedEventArgs e)
{
HttpContext.Current.Response.Redirect(string.Format("OrderCompleted.aspx?OrderNumber={0}&InstanceID={1}", txtOrderNumber.Text, lblWorkflowInstanceId.Text));
}
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack )
{
this.StartWorkflow();
this.btnCreateOrder.Enabled = true;
}
}
protected void btn_Click(object sender, EventArgs e)
{
Button currentButton = sender as Button;
Guid instanceId = new Guid(this.lblWorkflowInstanceId.Text);
OrderService orderService = (Application["WorkflowRuntime"] as WorkflowRuntime).GetService<OrderService>();
ManualWorkflowSchedulerService scheduler = (Application["WorkflowRuntime"] as WorkflowRuntime).GetService<ManualWorkflowSchedulerService>();
switch (currentButton.ID)
{
case "btnCreateOrder":
currentButton.Enabled = false;
orderService.RaiseOrderCreatedEvent(txtOrderNumber.Text, instanceId);
scheduler.RunWorkflow(instanceId);
lblOrderStatus.Text = GetCurrentState(instanceId);
this.btnProcessOrder.Enabled = true;
break;
case "btnProcessOrder":
currentButton.Enabled = false;
orderService.RaiseOrderProcessedEvent(txtOrderNumber.Text, instanceId);
scheduler.RunWorkflow(instanceId);
lblOrderStatus.Text = GetCurrentState(instanceId);
this.btnShipOrder.Enabled = true;
break;
case "btnShipOrder":
currentButton.Enabled = false;
orderService.RaiseOrderShippedEvent(txtOrderNumber.Text, instanceId);
scheduler.RunWorkflow(instanceId);
break;
}
}
}
8. Add a new web page named OrderCompleted.aspx to the web project.
9. Replace the content of the .aspx page with the following:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="OrderCompleted.aspx.cs" Inherits="OrderCompleted" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
<title>Order Completed</title>
</head>
<body>
<form id="form1" runat="server">
<div>
Order Completed<br />
<br />
<asp:Button ID="btnNewOrder" runat="server" OnClick="btnNewOrder_Click" Text="New Order" /></div>
</form>
</body>
</html>
10. Replace the content of the .aspx.cs page with the following:
using System;
using System.Web;
public partial class OrderCompleted : System.Web.UI.Page
{
protected void btnNewOrder_Click(object sender, EventArgs e)
{
HttpContext.Current.Response.Redirect("Default.aspx");
}
protected void Page_Load(object sender, EventArgs e)
{
lblOrderNumber.Text = HttpContext.Current.Request.QueryString["OrderNumber"];
lblWorkflowInstanceId.Text = HttpContext.Current.Request.QueryString["InstanceID"];
}
}
11. Add a Web.Config file to the web site and change the content to the following:
<?xml version="1.0"?>
<configuration xmlns="http://schemas.microsoft.com/.NetConfiguration/v2.0">
<configSections>
<section name="WorkflowRuntime" type="System.Workflow.Runtime.Configuration.WorkflowRuntimeSection, System.Workflow.Runtime, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</configSections>
<appSettings/>
<connectionStrings/>
<system.web>
<authentication mode="Windows"/>
<httpModules>
<add type="System.Workflow.Runtime.Hosting.WorkflowWebHostingModule, System.Workflow.Runtime, Version=3.0.00000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" name="WorkflowHost"/>
</httpModules>
<compilation debug="true">
<assemblies>
<add assembly="System.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
<add assembly="System.Drawing.Design, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
<add assembly="System.Transactions, Version=2.0.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"/>
<add assembly="Microsoft.Build.Tasks, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
<add assembly="System.Messaging, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
<add assembly="System.Runtime.Remoting, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
<add assembly="System.DirectoryServices, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
<add assembly="System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/>
<add assembly="Microsoft.Build.Utilities, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
<add assembly="Microsoft.Build.Framework, Version=2.0.0.0, Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A"/>
</assemblies>
</compilation>
</system.web>
<WorkflowRuntime Name="WorkflowServiceContainer">
<Services>
<add type="System.Workflow.Runtime.Hosting.ManualWorkflowSchedulerService, System.Workflow.Runtime, Version=3.0.00000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<add type="System.Workflow.Activities.ExternalDataExchangeService, System.Workflow.Activities, Version=3.0.00000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</Services>
</WorkflowRuntime>
</configuration>
- Add a Global.asax file to the web site and update the Application_Start and End to the following:
void Application_Start(object sender, EventArgs e)
{
// NOTE: This requires the configuration section to be named "WorkflowRuntime".
System.Workflow.Runtime.WorkflowRuntime workflowRuntime = new System.Workflow.Runtime.WorkflowRuntime("WorkflowRuntime");
Application["WorkflowRuntime"] = workflowRuntime;
workflowRuntime.StartRuntime();
}
void Application_End(object sender, EventArgs e)
{
System.Workflow.Runtime.WorkflowRuntime workflowRuntime = Application["WorkflowRuntime"] as System.Workflow.Runtime.WorkflowRuntime;
workflowRuntime.StopRuntime();
}
You should now be able to build and run the project.
On default you can enter and order number, a default is provided. When the page is loaded the workflow is started. Click the enabled button and the updated state will be displayed. When the workflow completes you are taken to the order completed page and given a chance to create another order.
Hope this helps you to better understand how to get this working. Let me know if you have any questions.
You can find the competed sample here.
- Create a new Web Site project.
- At step 10 you say replace the .cs file but the code that follows is markup not C# - can you post the missing code?
- Well having looked at the example, I assume it's just Response.Redirect("Default.aspx") in the click handler, so scrub that request
- Tom, where do you live? I owe you a beer! Heck, I'm so happy I might take you out to dinner! Thank you very much! You were right on with the scheduler recommendation from earlier. I didn't realize I had to call it again after the event is raised.
Thank you very much! - Well done, Tom! This is exactly what I have been scrathing my head over for the past three weeks! I had yet too find a clear-cut example of implementing a state workflow in asp.net and this is EXACTLY the type of example that I needed!
Forget the single beer, I'll send you a complete case! (Ok, not really, but it is the thought that counts, yeah?)
OOOPS!!!!!
Tom-
In the example that you posted, you are using the WorkflowWebRequestContext object, but this object was removed in Beta 2.2. In addition, when trying to follow your instructions with Beta 2.2, the Lab code also fails to compile. Can you offer any suggestions as to how to resolve?
Thanks.
EDIT:
OK--
To resolve the code in the Labs and make it compile, jsut make public the
OrderSender and.OrderEventArgs objects.
I am still looking at the WorkflowWebRequestContext problem, but I THINK that there will be no recourse but to implement the workflow as a web service. Grrrr... - Oops. Cut and paste snafu. I updated step 10 to have the correct code.
- You are able to pass parameters to a StateMachineWorkflow just like with a SequentialWorkflow, and you have access to out parameters in the WorkflowCompletedEventArgs.
Yes, WorkflowWebRequestContext has been removed. Now you need to create and store the runtime on you own, as an Application or Session object.
UPDATED
I modified the sample to remove WorkflowWebRequestContext. Now the WorkflowRuntime is created in Application_Start and stored in the Application state.
Tom,
in this case, I don't think we need the DefautWorkflowTransactionService in the Web.config WorkflowServiceContainer
Serge Luca
Guidance
Tom,
I've tried to integrated the StateMachineTracking service in your asp.net sample
as soon as I register the StateMachineTracking in the web.config , WorkflowWebRequestContext.Current returns null in the StartWorkflow() function.
And according to my debbuger, the StateMachineTrackingService object is loaded with the appropriate container parameter
Herer is web.config section
<WorkflowRuntime Name="WorkflowServiceContainer">
<Services>
<add type="System.Workflow.Runtime.Hosting.ManualWorkflowSchedulerService, System.Workflow.Runtime, Version=3.0.00000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
<add type="StateMachineTracking.StateMachineTrackingService, StateMachineTracking"/>
<add type="System.Workflow.Activities.ExternalDataExchangeService, System.Workflow.Activities, Version=3.0.00000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
</Services>
Any idea ?
Serge Luca
Guidance, Belgium
www.redwood.be
oops, sorry Tom, I'm still using the WWF b2; according to your last post, the WorkflowWebRequest has been removed in the beta 2.2; I've just noticed it right now...

Serge Luca
Guidance, Belgium
Hi Serge-
I do not see the connection string as being in your web.config. My belief (based on encountering the same situation you have) is that you need the ConnectionString parameter within the <add>. Something like this:
<
add type="System.Workflow.Runtime.Tracking.SqlTrackingService, System.Workflow.Runtime, Version=3.0.00000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionString="server=<<SERVERNAME>>;integrated security=true;database=<<DatabaseName>>;/>I had tried usign the CommonParameters tag but was not getting the connection, but after including the ConnectionString in the <add>, it worked
-Peter
er...I just realized on the machine I encountered this issue on it was using Beta 2.0 and not Beta 2.2 so your milage may vary. Sorry.
Hi Peter,
thank you for your post; however in this case, the tracking service used here (lab 4) is not based on the db as SqlTrackingService does; it's another class derived from TraackingService which uses another channel.
I don't have any db connection problem at all here;but anyway, I'll move to beta 2.2

Serge Luca
Guidance, Belgium
Whoops.
My mistake. That's right, Lab 4 does use that service to capture state change events and it is NOT a db tracking service.
Sigh...shame on me for not reading your post correctly. ;-)
(Sorry for the cross post, I went to eat something and fishined the post later...:P)
Looking at the samples inside samples.zip I found that the HelpDeskSupport application uses another way to get the reference to the workflow.
This is what I did based on the sample mentioned above (it´s very similar...):
I store the runtime in the HttpContext (Cache)
private void InitWorkflowRuntime()
{
//**PRIMERA FORMA DE INICIALIZAR EL WORKFLOWRUNTIME**//
workflowRuntime = (WorkflowRuntime)HttpContext.Current.Cache[WorkflowRuntimeCacheKey];
if (workflowRuntime == null)
{
workflowRuntime = new WorkflowRuntime();
HttpContext.Current.Cache[WorkflowRuntimeCacheKey] = workflowRuntime;
}
//agregamos el servicio ExternalDataExchangeService al Runtime
ExternalDataExchangeService externalDataExchange = new ExternalDataExchangeService();
workflowRuntime.AddService(externalDataExchange);
//Agregamos nuestra clase marcada como ExternalDataExchange al ExternalDataExchangeService
formService = new FormServices.FormService();
externalDataExchange.AddService(formService);
workflowRuntime.StartRuntime();}
So, then when I have the runtime ready, I start the Workflow returning the InstanceId:
private System.Guid StartFormWorkflow()
{
//cargamos el Assembly con el Workflow;
System.Reflection.Assembly assembly = System.Reflection.Assembly.Load("FormWorkflow");//Tomamos el Type del Assembly que cargamos;
System.Type workflowType = assembly.GetType("FormWorkflow.FormSMWorkflow");if (workflowType == null)
return System.Guid.Empty;//Creamos una instancia del workflow tomando como parametro el Type
WorkflowInstance workflowInstance = workflowRuntime.CreateWorkflow(workflowType);//Creamos WorkflowStateMachineInstance para inspeccionar el workflow.
StateMachineWorkflowInstance smwi = new StateMachineWorkflowInstance(workflowRuntime, workflowInstance.InstanceId);//Iniciamos la instancia;
workflowInstance.Start();
//Agregamos la instancia a nuestro diccionario
stateMachineInstances.Add(workflowInstance.InstanceId.ToString(), smwi);//Add the Workflow instanceId to the session object
this.Session["workflowInstanceId"] = workflowInstance.InstanceId;//Devolvemos la instancia del workflow;
return workflowInstance.InstanceId;
}All this is happens when a Button is clicked:
protected void btnOrder_Click(object sender, EventArgs e)
{
string orderId = txtOrderId.Text;//Start the workflow
System.Guid workflowInstance = this.StartFormWorkflow();
//Raise FormCreated event
formService.RaiseFormCreatedEvent(orderId, workflowInstance);lstWorkflows.Items.Add(new ListItem(workflowInstance.ToString(), orderId));
}All this is working, in fact I have a Datagrid showing the instances I've created;
public DataTable GetWorkflows()
{
DataTable dt = new DataTable("WorkflowDataTable");
dt.Columns.Add("ID");
dt.Columns.Add("Type");
//dt.Columns.Add("WorkflowState");
ReadOnlyCollection<WorkflowInstance> wfInstances = workflowRuntime.GetLoadedWorkflows();foreach (WorkflowInstance workflowInstance in wfInstances)
{
dt.Rows.Add(new object[] {workflowInstance.InstanceId, workflowInstance.GetWorkflowDefinition().GetType().ToString()});
}
return dt;
}protected void btnDataBindGrid_Click(object sender, EventArgs e)
{
GridWorkflows.DataSource = this.GetWorkflows();
GridWorkflows.DataBind();
}Now, with Tom's example I have a little bit clear how to use the ManualWorkflowScheduler.
I Can´t post the whole thing because it´s a terrible mess, but the point if i this is a correct solution to the missing class in Beta 2.2Suggestions, comments are welcome.
Best regards.Tom,
The example you provided with ASP.NET raising events using Lab 4 (State Workflow) is excellent (http://blogs.msdn.com/tomlake/archive/2006/05/02/588617.aspx). I need the same, but as xoml. The xoml example you provided does not use typelibrary binding and does not raise events (I am using WWF Beta 2). Can you please modify this example to load xoml and rules files as your other xoml example?
I look forward to hearing from you soon.
Thanks in advance.
Sincerely,
John Portnov
Tom,
I modified your ASP.NET and XOML loading example. I have 3 states, 1 Approval event. I want to load the xoml workflow as xoml and raise an event. Below are the details.
---------------------------------------------------------------------------------------------------------------
-----------------------------------------
The error I get when calling CreateWorklow is:
-----------------------------------------
WorkflowInstance workflowInstance = workflowRuntime.CreateWorkflow(xomlReader, ruleReader, parameters);The workflow failed validation. {error 278: Activity 'EmailApprovedHandleExternalActivity' validation failed: Property 'InterfaceType' is not set.}
-------------------------------------
Below is the code that calls the xoml workflow:
----------------------------------------
// Get a reference to the Workflow Runtime through the WorkflowWebHostingModule
// Http Module.
//
// NOTE: This requires the configuration section to be named "WorkflowRuntime".
WorkflowRuntime workflowRuntime = Application["WorkflowRuntime"] as WorkflowRuntime;// Now get a refernece to the ManualWorkflowSchedulerService
ManualWorkflowSchedulerService scheduler =
workflowRuntime.GetService<ManualWorkflowSchedulerService>();ExternalDataExchangeService dataService = workflowRuntime.GetService<ExternalDataExchangeService>();
iServeLocalServices.iServeTypeLib orderService = workflowRuntime.GetService<iServeLocalServices.iServeTypeLib>();
if (orderService == null)
{
orderService = new iServeLocalServices.iServeTypeLib();
dataService.AddService(orderService);
}//// Attach to the WorkflowCompleted & WorkflowTerminated events
workflowRuntime.WorkflowCompleted += new EventHandler<WorkflowCompletedEventArgs>(WorkflowRuntime_WorkflowCompleted);
//''''''''''''''''
XmlTextReader xomlReader = new XmlTextReader(@"C:\XOML\Tom\Xoml+activation+samples\Xoml activation with state machine workflow Beta2\WorkflowConsoleApplication11\StateMachineXomlActivationSample.xoml");
XmlTextReader ruleReader = new XmlTextReader(@"C:\XOML\Tom\Xoml+activation+samples\Xoml activation with state machine workflow Beta2\WorkflowConsoleApplication11\StateMachineXomlActivationSample.rules");string _to = "John_Portnov@adp.com";
string _from = _to;
string _toMgr = _to;
string _toAdm = _to;
string _subj = "Subject 123";
string _body = "Body Test 12345";Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("PurchaseOrder", new BaseStateMachineLibrary.PurchaseOrder(5001,
_to, _from, _toMgr, _toAdm, _subj, _body));WorkflowInstance workflowInstance = workflowRuntime.CreateWorkflow(xomlReader, ruleReader, parameters);
workflowInstance.Start();
xomlReader.Close();
ruleReader.Close();// Now run the workflow. This is necessary when
// ...using the ManualWorkflowSchedulerService
scheduler.RunWorkflow(workflowInstance.InstanceId);----------------------------------------
-------------------------------------
The xoml I am using is below:
-------------------------------------<ns0:BaseWorkflow PurchaseOrder="{p1:Null}" InitialStateName="InitialState" x:Name="Workflow1" DynamicUpdateCondition="{x:Null}" CompletedStateName="CompletedState" xmlns="http://schemas.microsoft.com/winfx/2006/xaml/workflow" xmlns:ns1="clr-namespace:ADPSendEmail;Assembly=ADPSendEmail, Version=3.0.0.0, Culture=neutral, PublicKeyToken=null" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:p1="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:ns0="clr-namespace:BaseStateMachineLibrary;Assembly=BaseStateMachineLibrary, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null">
<StateActivity x:Name="InitialState">
<StateInitializationActivity x:Name="EmailCreatedEvent">
<CodeActivity x:Name="codeActivity1" ExecuteCode="{ActivityBind Workflow1,Path=codeActivity_ExecuteCode}" />
<CodeActivity x:Name="CreateEmailRequest" ExecuteCode="{ActivityBind Workflow1,Path=CreateEmailRequest_ExecuteCode}" />
<ns1:ADPWSSendEmail toA="{ActivityBind Workflow1,Path=EmailCreatedSendEmailActivity_toA1}" x:Name="EmailCreatedSendEmailActivity" subj="{ActivityBind Workflow1,Path=EmailCreatedSendEmailActivity_subjA1}" name="{ActivityBind Workflow1,Path=EmailCreatedSendEmailActivity_nameA1}" Description="Send Manager email notification" body="{ActivityBind Workflow1,Path=EmailCreatedSendEmailActivity_bodyA1}" fromA="{ActivityBind Workflow1,Path=EmailCreatedSendEmailActivity_fromA1}" xfilepath="{x:Null}" />
<IfElseActivity x:Name="isOver5000">
<IfElseBranchActivity x:Name="greaterThan5000">
<IfElseBranchActivity.Condition>
<RuleConditionReference ConditionName="Condition1" />
</IfElseBranchActivity.Condition>
<CodeActivity x:Name="greaterThan" ExecuteCode="{ActivityBind Workflow1,Path=Greater}" />
</IfElseBranchActivity>
<IfElseBranchActivity x:Name="lessThan5000">
<CodeActivity x:Name="lessThan" ExecuteCode="{ActivityBind Workflow1,Path=Less}" />
</IfElseBranchActivity>
</IfElseActivity>
<SetStateActivity x:Name="setApprovalState" TargetStateName="EmailApprovalState" />
</StateInitializationActivity>
</StateActivity>
<StateActivity x:Name="EmailApprovalState">
<EventDrivenActivity x:Name="EmailApprovedEvent">
<HandleExternalEventActivity Invoked="EmailApproved_Invoked" x:Name="EmailApprovedHandleExternalActivity" EventName="EmailApproved" InterfaceType="{x:Type iServeLocalServices.iServeInterface}">
<HandleExternalEventActivity.ParameterBindings>
<WorkflowParameterBinding ParameterName="e">
<WorkflowParameterBinding.Value>
<ActivityBind Name="Workflow1" Path="OrderEvtArgs" />
</WorkflowParameterBinding.Value>
</WorkflowParameterBinding>
<WorkflowParameterBinding ParameterName="sender">
<WorkflowParameterBinding.Value>
<ActivityBind Name="Workflow1" Path="OrderSender" />
</WorkflowParameterBinding.Value>
</WorkflowParameterBinding>
</HandleExternalEventActivity.ParameterBindings>
</HandleExternalEventActivity>
<ns1:ADPWSSendEmail toA="{ActivityBind Workflow1,Path=EmailApprovedSendEmailActivity_toA1}" x:Name="EmailApprovedSendEmailActivity" subj="{ActivityBind Workflow1,Path=EmailApprovedSendEmailActivity_subj1}" name="{ActivityBind Workflow1,Path=EmailApprovedSendEmailActivity_name1}" Description="Send approved email" body="{ActivityBind Workflow1,Path=EmailApprovedSendEmailActivity_body1}" fromA="{ActivityBind Workflow1,Path=EmailApprovedSendEmailActivity_fromA1}" xfilepath="{ActivityBind Workflow1,Path=EmailApprovedSendEmailActivity_xfilepath1}" />
<SetStateActivity x:Name="EmailApprovedSetStateActivity" TargetStateName="CompletedState" />
</EventDrivenActivity>
</StateActivity>
<StateActivity x:Name="CompletedState" />
</ns0:BaseWorkflow>
-------------------------------------Do you see anything wrong with the xoml syntax? Anything wrong with the code calling the xoml?
I look forward to hearing from you soon.
Thanks in advance,
John Portnov
When you create the runtime you need to create a TypeProvider and add references to your dependent assemblies like the following.
TypeProvider typeProvider = new TypeProvider(workflowRuntime);
typeProvider.AddAssembly(typeof(iServeLocalServices.iServeInterface).Assembly);
typeProvider.AddAssembly(typeof(ADPWSSendEmail).Assembly);
workflowRuntime.AddService(typeProvider);Tom,
Thanks. After adding the typeprovider code, I did not get an error when executing the CreateWorkflow line. Now, I just need to be able to successfully raise an event (ie., using "RaiseCreateEmailEvent" now). Below is my code and the web.config section that I am using. Do you see anything that would be generating the following error:
{"Event \"EmailCreated\" on interface type \"iServeLocalServices.iServeInterface\" for instance id \"12e87dc5-6fe7-4374-bb64-856a9b20614d\" cannot be delivered."}
Calling code:
------------------
protected void btn_Click(object sender, EventArgs e){
Button currentButton = sender as Button; Guid instanceId = new Guid(this.lblWorkflowInstanceId.Text); //OrderService orderService = (Application["WorkflowRuntime"] as WorkflowRuntime).GetService<OrderService>();iServeLocalServices.
iServeTypeLib orderService = (Application["WorkflowRuntime"] as WorkflowRuntime).GetService<iServeLocalServices.iServeTypeLib>(); ManualWorkflowSchedulerService scheduler = (Application["WorkflowRuntime"] as WorkflowRuntime).GetService<ManualWorkflowSchedulerService>(); switch (currentButton.ID){
case "btnCreateOrder":currentButton.Enabled =
false;orderService.RaiseStateEvent(instanceId,
"RaiseEmailCreatedEvent"); //orderService.RaiseOrderCreatedEvent(txtOrderNumber.Text, instanceId);scheduler.RunWorkflow(instanceId);
//lblOrderStatus.Text = GetCurrentState(instanceId); this.btnProcessOrder.Enabled = true; break; case "btnProcessOrder":currentButton.Enabled =
false;orderService.RaiseStateEvent(instanceId,
"RaiseEmailApprovedEvent"); //orderService.RaiseOrderProcessedEvent(txtOrderNumber.Text, instanceId);scheduler.RunWorkflow(instanceId);
//lblOrderStatus.Text = GetCurrentState(instanceId);------------------
------------------
Web.config settings
-----------------------------
<
WorkflowRuntime Name="WorkflowServiceContainer"><
CommonParameters><
add name="ConnectionString" value="Initial Catalog=tracking;Data Source=databaseserver;uid=user1;pwd=password1;"/></
CommonParameters><
Services><
add type="System.Workflow.Runtime.Hosting.ManualWorkflowSchedulerService, System.Workflow.Runtime, Version=3.0.00000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/><
add type="System.Workflow.Runtime.Hosting.SharedConnectionWorkflowTransactionService, System.Workflow.Runtime, Version=3.0.00000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/><
add type="System.Workflow.Activities.ExternalDataExchangeService, System.Workflow.Activities, Version=3.0.00000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/></
Services></
WorkflowRuntime>-----------------------------
Error----{"Event \"EmailCreated\" on interface type \"iServeLocalServices.iServeInterface\" for instance id \"12e87dc5-6fe7-4374-bb64-856a9b20614d\" cannot be delivered."}
The InnerException is this:
{"The workflow hosting environment does not have a persistence service as required by an operation on the workflow instance \"b7c621a9-0ec4-460b-9d4f-0e39f9661bd3\"."}
The line inside the type library that fails is this:
--------------------------------------------------
public void RaiseStateEvent(Guid instanceId, string eventName){
if ((EmailCreated != null) && (eventName == "RaiseEmailCreatedEvent"))EmailCreated(
null, new OrderEventArgs(instanceId)); if ((EmailApproved != null) && (eventName == "RaiseEmailApprovedEvent"))EmailApproved(
null, new OrderEventArgs(instanceId)); if ((EmailRejected != null) && (eventName == "RaiseEmailRejectedEvent"))EmailRejected(
null, new OrderEventArgs(instanceId)); if ((EmailCanceled != null) && (eventName == "RaiseEmailCancelledEvent"))EmailCanceled(
null, new OrderEventArgs(instanceId)); }-------------------------------------------
You were able to raise events with your ASP.NET with state workflow sample (without having persistence service in the web.config). Why can't I do the same with xoml state workflow???????
Why am I getting????????????????
{"The workflow hosting environment does not have a persistence service as required by an operation on the workflow instance \"efa8c5dc-de82-478c-9c29-a7b66fb061e7\"."}
Confused,
John Portnov
- This sample almost exactly answers some of my issues with the ASP.Net and a State Machine workflow.
BUT!!!
I have a delay in one of my state machine states event driven activity!! This delay handles the escalation of the workflow not being actioned in a certain time frame.
I know the Manual Scheduler Service has issues with delay activities in Beta 2 and Beta 2.2, so I had implemented the Default Scheduler as a temporary work around,as suggested in several Blog postings. But I was getting no where, so I went back to this sample to test some stuff out.
If you tinker with this sample and set it up to use persistance and tracking with the Manual Scheduler everything works fine.
But, if you set it to use the default scheduler then you get some interesting behaviour!
First off:- GetCurrentState(Guid instanceId) method returns null and not the expected string "WaitingForOrderState".
- Then on clicking the buttons moves the workflow but the displayed text for the state appears to be 1 state behind where it should be.
- Unfortunately you will need to wait. Because you are no longer running the workflow on the same thread as the ASP then there will be some timing issues, like the ones you have noted.
Hi,
This is my first venture in to WWF, this example is perfect for my needs.
How would it be adapted to incorporate a back button?
- From here, how much more needs to be done to reimplement the StateMachineTracking service?
Hello Tom,
I read your all blogs about implementing StateMachineWorkflow using ASP.net. Also I have gone through example of "ASPNETStateMachineWorkflow". I am trying to develop small application using StateMachineWorkflow in ASP.NET. Before, I start asking Question to you, I describe why I chose StateMachineWorkflow in ASP.NET.
In my application, individual user can request order creation. Each created order should be validated by Admin. Admin can authorize or cancel this order. Admin can see this request immediately, after placing user order request. And from list of different users pending request, he can authorize or cancel individual user request. This notification should be store somewhere like SQL DB. So later user or admin can view all request status.
As per my application scenario, I thought StateMachineWorkflow workflow style is perfect match with ASP.NET. Even I read that implementing StateMachineWorkflow is good practice with ASP.NET. I am using SQL Server Database to store status of each user request.
(Start State)
1) "WaitingForOrderCreationState" (StateActivity)
Event - "OrderCreatedEvent" (EventDrivenActivity) and Receive "OrderOpenState" (SetStateActivity)
2) "OrderOpenState" (StateActivity)
Event - "ValidateOrderEvent" (EventDrivenActivity) and Receive "NotificationState" (SetStateActivity)
3) "NotificationState" (StateActivity)
Event - "StoreNotificationEvent" and Receive "OrderCompltedState" (SetStateActivity)
4) "OrderCompltedState" (StateActivity)
(Workflow completes)
Be frank, still I am not clear that why should I use Workflow for my above application scenario? if I need to use, then how workflow will be useful for my application? Can you explain me about it?
As I know that you have good working knowledge about Workflow with ASP.NET.
Thanking you in Advance.
Satyam AMIN
hi luca
im trying to create StateMachineTrackingService instance according to Lab 4 but unable to create. Can u please tell if im missing something in that
hi
have u understood the lab ?
if u have created StateMachineTrackingServices instance..please help me
i have included all the namespaces but still not getting the instance of StaeMachineTrackingServices.
please help me
Hi All,
Nice to see the articles abt the Work flow foundation. I am working on asp.net since the last few months. As i am very new to workflow tech i want to develop a small application using ASP.Net with State Machine workflow.
Can anybody mail me an simple ASP.Net Project having two ot three web forms and using the State Machine Workflow in ASP.Net to satya.smiles2000@gmail.com.
I have gone thru diff sites but those are not useful. Pls do the needful.
Thanks in advance.
Hi All,
Nice to see the articles abt the Work flow foundation. I am working on asp.net since the last few months. As i am very new to workflow tech i want to develop a small application using ASP.Net with State Machine workflow.
Can anybody mail me an simple ASP.Net Project having two ot three web forms and using the State Machine Workflow in ASP.Net to satya.smiles2000@gmail.com.
I have gone thru diff sites but those are not useful. Pls do the needful.
Thanks in advance.
- Take a look at the sample at http://blogs.msdn.com/tomlake/archive/2006/05/17/examples-of-using-persistence-and-tracking-in-asp-net.aspx.
Hello, I'm writing this reply because the WF team has just release a new sample that directly address the PageFlow problem. It greatly simplifies the integration of ASP.Net and WF when WF is used to guide the navigation of pages. I'm interested in knowing whether this sample would be of use to you. To download the sample, follow the link in Matt Winkler's blog about PageFlow.
http://blogs.msdn.com/mwinkle/archive/2007/06/07/introducing-the-pageflow-sample.aspx
Thanks.
Shelly Guo
Hi Tom,
What is the diffrence between adding the workflowruntine section in the web.config file and adding the assembly like this in the global.asax.
Thanks,
Satya.
- No difference, just two ways you can do the same thing. For an exe if would mean you could change the services added without rebuilding the program but for an website you would need to re-cycle IIS either way so that Applicaton_Start would run again, assuming that is where you create and store the workflow runtime.


