Ask a questionAsk a question
 

AnswerASP.NET And a State Machine Workflow

  • Wednesday, April 12, 2006 4:21 PMsenfo Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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

  • Thursday, April 20, 2006 12:25 AMTom LakeMSFTUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

    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.

     

    1. Create a new Web Site project.
    2. Add a new Empty Workflow Project to the solution.
    3. Add the empty workflow project as a reference to the web site.
    4. Add IOrderService.cs and OrderService.cs from the hands on lab 4, that can be found under Resources\OrderLocalServices, to the workflow project.
    5. Add Workflow1.cs also from the hands on lab 4, that can be found under Completed\Exercise 2\OrderWorkflows\OrderWorkflows, to the workflow project.
    6. 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 &nbsp; &nbsp;

            <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 />

            &nbsp;<asp:Button ID="btnCreateOrder" runat="server" Text="Create Order" OnClick="btn_Click" TabIndex="2" Enabled="False" />

            &nbsp; &nbsp;

            <asp:Button ID="btnProcessOrder" runat="server" OnClick="btn_Click" TabIndex="3"

                Text="Process Order" Enabled="False" />

            &nbsp; &nbsp;

            <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>

     

    1. 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.

  • Friday, April 21, 2006 1:57 AMTom LakeMSFTUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

    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.

  • Sunday, June 10, 2007 3:00 AMTom LakeMSFTUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

All Replies

  • Wednesday, April 12, 2006 5:42 PMMarkuzt Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    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.

  • Wednesday, April 12, 2006 9:09 PMsenfo Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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...
  • Wednesday, April 12, 2006 9:31 PMTom LakeMSFTUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    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

  • Friday, April 14, 2006 2:58 PMsenfo Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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...
  • Friday, April 14, 2006 5:39 PMTom LakeMSFTUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    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>

  • Monday, April 17, 2006 9:03 PMsenfo Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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.
  • Tuesday, April 18, 2006 2:07 AMsenfo Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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.
  • Tuesday, April 18, 2006 3:12 PMGUYO Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    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

     

     

  • Wednesday, April 19, 2006 10:00 PMsenfo Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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 ).
  • Thursday, April 20, 2006 12:25 AMTom LakeMSFTUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

    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.

     

    1. Create a new Web Site project.
    2. Add a new Empty Workflow Project to the solution.
    3. Add the empty workflow project as a reference to the web site.
    4. Add IOrderService.cs and OrderService.cs from the hands on lab 4, that can be found under Resources\OrderLocalServices, to the workflow project.
    5. Add Workflow1.cs also from the hands on lab 4, that can be found under Completed\Exercise 2\OrderWorkflows\OrderWorkflows, to the workflow project.
    6. 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 &nbsp; &nbsp;

            <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 />

            &nbsp;<asp:Button ID="btnCreateOrder" runat="server" Text="Create Order" OnClick="btn_Click" TabIndex="2" Enabled="False" />

            &nbsp; &nbsp;

            <asp:Button ID="btnProcessOrder" runat="server" OnClick="btn_Click" TabIndex="3"

                Text="Process Order" Enabled="False" />

            &nbsp; &nbsp;

            <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>

     

    1. 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.

  • Thursday, April 20, 2006 11:11 AMHowardRichards Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    At step 10 you say replace the .cs file but the code that follows is markup not C# - can you post the missing code?
  • Thursday, April 20, 2006 11:16 AMHowardRichards Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Well having looked at the example, I assume it's just Response.Redirect("Default.aspx") in the click handler, so scrub that request
  • Thursday, April 20, 2006 3:59 PMsenfo Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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!
  • Thursday, April 20, 2006 4:34 PMPeter Y Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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...
  • Thursday, April 20, 2006 6:02 PMTom LakeMSFTUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    Oops.  Cut and paste snafu.  I updated step 10 to have the correct code.
  • Friday, April 21, 2006 1:48 AMTom LakeMSFTUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    You are able to pass parameters to a StateMachineWorkflow just like with a SequentialWorkflow, and you have access to out parameters in the WorkflowCompletedEventArgs.
  • Friday, April 21, 2006 1:57 AMTom LakeMSFTUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

    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.

  • Friday, April 21, 2006 8:23 AMSerge Luca [MVP]MVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    Tom,

    in this case, I don't think we  need the DefautWorkflowTransactionService in the Web.config WorkflowServiceContainer

    Serge Luca

    Guidance

    www.redwood.be

  • Friday, April 21, 2006 3:34 PMSerge Luca [MVP]MVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    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

  • Friday, April 21, 2006 3:39 PMSerge Luca [MVP]MVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    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

    www.redwood.be

  • Friday, April 21, 2006 3:40 PMPeter Y Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    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.

  • Friday, April 21, 2006 4:39 PMSerge Luca [MVP]MVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    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

    www.redwood.be

  • Friday, April 21, 2006 5:23 PMPeter Y Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    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. ;-)

  • Friday, April 21, 2006 5:42 PMMarkuzt Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    (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.2

    Suggestions, comments are welcome.
    Best regards.

  • Thursday, May 04, 2006 9:19 PMJohn Portnov Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    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

  • Friday, May 05, 2006 1:53 PMJohn Portnov Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    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

     

  • Friday, May 05, 2006 4:02 PMTom LakeMSFTUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    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);
  • Friday, May 05, 2006 8:05 PMJohn Portnov Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    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

  • Thursday, May 11, 2006 8:13 AMNozTheGK Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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.
    Does anyone have any ideas as to how to fix this, or have I got to wait until the next public drop of the WF?
  • Thursday, May 11, 2006 2:27 PMTom LakeMSFTUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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.
  • Monday, May 22, 2006 12:35 PMM_Laz Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    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?

  • Monday, May 22, 2006 9:29 PMsenfo Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    From here, how much more needs to be done to reimplement the StateMachineTracking service?
  • Monday, July 10, 2006 3:12 PMsatyam amin Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    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

  • Monday, January 08, 2007 9:23 AMPriyanka Choughule Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    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

     

  • Monday, January 08, 2007 9:32 AMPriyanka Choughule Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    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

  • Wednesday, June 06, 2007 12:33 PMSatya Puvvada Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    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.

  • Wednesday, June 06, 2007 12:39 PMSatya Puvvada Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    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.

  • Sunday, June 10, 2007 3:00 AMTom LakeMSFTUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer
  • Tuesday, June 12, 2007 12:27 AMShelly Guo - MSFTMSFTUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    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

     

  • Thursday, June 21, 2007 12:59 PMSatya Puvvada Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    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.

  • Thursday, June 21, 2007 11:48 PMTom LakeMSFTUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    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.