HOWTO: Host a Workflow Service in IIS (the whole story)

 

  1. I started out in my attempt to figure out how to host a workflow service in IIS by looking at the following MSDN article:

  2.  

  3. How to: Host a Workflow Service in IIS

  4. http://msdn2.microsoft.com/en-us/library/bb675264.aspx

  5.  

  6. This gave me 'most' of the information I needed but as is usual for me, it's the small missing pieces of information that end up creating the most grief for me.  So, I thought I would put down all the steps it took, from start to finish in hopes that this may help someone else.

  7.  

  8. What we are going to be doing is hosting an existing, pre-compiled workflow as a service in IIS (on Vista). What this implies is that we are going to create a workflow library project.  This is what tripped me up because there is a particular way you need to do this otherwise you have to end up adding a lot of your own code.

  9.  

  10. Setting up things in IIS

  11. First, you have to make sure you have IIS installed on the machine and that it's running. Therefore you need to go through the Control Panel | Programs and Features | Turn Windows features on and off and select Internet Information Services.
     

  12. Open the Internet Information Services Manager by going to Control Panel | Administrative Tools | Internet Information Services (IIS) Manager.
     

  13. Create a new virtual directory by right clicking on Default Web Site and selecting 'Add Virtual Directory'. Here you will need to give this virtual directory an alias name and point it to a physical directory on the machine. For the same of our sample, I'm going to give the alias the name of 'IISWorkflowHost' and point it to a directory I've already named on my machine 'C:\IISHostForWorkflow'. I have named the alias and directory differently the help you understand they don't need to necessary be named the same thing and to also show how we reference our service later on in the blog.
     

  14. Double click on the Authentication icon in the IIS section. You need to enable Windows Authentication.

     

  15. Looking in the IIS Manager (left hand tree view), you will see the new folder for IISWorkflowHost. This is just a directory however and to get this to work correctly you need to right click on the directory and select 'Convert to Application'. You can just select OK from the dialog box that pops up or you can change the AppPool if desired. For this example, we'll just accept the defaults. This step was one of the things I was missing when I tried to get it working initially.
     

  16. Create a Bin directory under the C:\IISHostForWorkflow directory. This is where you will end up putting the workflow assembly that we will create later. Before we do any further additions to our virtual directory, let's go create our workflow service.

     

     

    Creating the Workflow Service

  17. Open Visual Studio 2008 and select File | New | Project | Visual C# | WCF | Sequential Workflow Service Library. Give it the name of WFToHostInIIS and put it in the directory C:\IISHostedWFExample. Select OK.
     

  18. Rename Workflow1.cs to OrderProcessWF.cs.
     

  19. Rename IWorkflow1.cs (the contract file) to IProductOrder.cs.
     

  20. We need to replace ALL the code in the IProductOrder.cs file with:

     

     

    using System;

    using System.Collections.Generic;

    using System.Linq;

    using System.Text;

    using System.Runtime.Serialization;

    using System.ServiceModel;

     

    namespace WFToHostInIIS

    {

        //Service contract

        [ServiceContract(Namespace = "http://ncdevguyblog/Workflow")]

        public interface IProductOrder

        {

            [OperationContract]

            void SubmitOrder(OrderInfo order);

     

            [OperationContract]

            OrderInfo GetOrder(string orderID);

        }

     

        //Data type (contract)

        [DataContract]

        public class OrderInfo

        {

            private string orderID = default(string);

            private string fName = default(string);

            private string lName = default(string);

     

            [DataMember]

            public string OrderID

            {

                get { return orderID; }

                set { orderID = value; }

            }

     

            [DataMember]

            public string FName

            {

                get { return fName; }

                set { fName = value; }

            }

     

            [DataMember]

            public string LName

            {

                get { return lName; }

                set { lName = value; }

            }

        }

    }

     

    This service exposes two methods, one for submitting an order, the other for getting order info. For simplicity, we are only going to implement SubmitOrder.

     

  21. Open OrderProcessWF in the workflow designer. Click on the Receive activity that you see on the designer surface and rename it to RcvSubmitOrder.
     

  22. Click on the ServiceOperationInfo property, select the ellipse on the right hand side and Import the IProductOrder contract. You may not actually need to import it, but if the SubmitOrder method doesn't show up in the Operations list, you'll need to import.
     

  23. Select the SubmitOrder operation. Select OK.
     

  24. Click on the 'order' parameter and then select the ellipse that will take you to the property/field binding dialog box. Select Bind to a new Member tab and then create a new property named WFOrderInfo. Select OK. 

 

  1. Open the app.config file. Everywhere you see 'IWorkflow1', replace that with 'IProductOrder'.
     

  2. For the <service name="WFToHostInIIS.Workflow1", change this to <service name="WFToHostInIIS.OrderProcessWF".
     

  3. For the base addresses:

              <baseAddresses>

                <add baseAddress="http://localhost:8731/Design_Time_Addresses/WFToHostInIIS/Workflow1/" />

              </baseAddresses>

     

    Change this to:
     

              <baseAddresses>

                <add baseAddress="http://localhost:8731/Design_Time_Addresses/WFToHostInIIS/OrderProcessWF/" />

              </baseAddresses>

     

    Note that your port number may be different in your case. This does not matter.  In the end, your app.config would look something like this:

     

    <?xml version="1.0" encoding="utf-8" ?>

    <configuration>

      <system.web>

        <compilation debug="true" />

      </system.web>

      <system.serviceModel>

        <services>

          <service name="WFToHostInIIS.OrderProcessWF" behaviorConfiguration="WFToHostInIIS.Workflow1Behavior">

            <host>

              <baseAddresses>

                <add baseAddress="http://localhost:8731/Design_Time_Addresses/WFToHostInIIS/OrderProcessWF/" />

              </baseAddresses>

            </host>

            <endpoint address=""

                      binding="wsHttpContextBinding"

                      contract="WFToHostInIIS.IProductOrder">

              <!--

                  Upon deployment, the following identity element should be removed or replaced to reflect the

                  identity under which the deployed service runs.  If removed, WCF will infer an appropriate identity

                  automatically.

              -->

              <identity>

                <dns value="localhost"/>

              </identity>

            </endpoint>

            <endpoint address="mex"

                      binding="mexHttpBinding"

                      contract="IMetadataExchange" />

          </service>

        </services>

        <behaviors>

          <serviceBehaviors>

            <behavior name="WFToHostInIIS.Workflow1Behavior"  >

              <serviceMetadata httpGetEnabled="true" />

              <serviceDebug includeExceptionDetailInFaults="false" />

              <serviceCredentials>

                <windowsAuthentication

                    allowAnonymousLogons="false"

                    includeWindowsGroups="true" />

              </serviceCredentials>

            </behavior>

          </serviceBehaviors>

        </behaviors>

      </system.serviceModel>

    </configuration>

     

     

    Finishing the configuration for IIS

  4. Since we are still within Visual Studio, we are going to create a new web.config file to put in the virtual directory/application we created. Just choose File | New | File | Web | Web Configuration File. Now what you are going to see is a configuration file that has a LOT of things that you don't need for this example. Just replace the entire contents of this file with:
     

    <?xml version="1.0" encoding="UTF-8"?>

    <configuration>

       

    </configuration>
     

  5. We need to add our own code to the web.config file within the <configuration> section. Paste in the following code:

    <system.serviceModel>

    <services>

    <service name="WFToHostInIIS.OrderProcessWF" behaviorConfiguration="ServiceBehavior">

    <endpoint address="ContextOverHttp" binding="wsHttpContextBinding" contract="WFToHostInIIS.IProductOrder" />

    </service>

    </services>

     

    <behaviors>

    <serviceBehaviors>

    <behavior name="ServiceBehavior">

    <serviceMetadata httpGetEnabled="true" />

    <serviceDebug includeExceptionDetailInFaults="true" />

    <serviceCredentials>

    <windowsAuthentication allowAnonymousLogons="false" includeWindowsGroups="true" />

    </serviceCredentials>

    <!-- Comment out the following behavior to disable persistence store -->

    <workflowRuntime name="WorkflowServiceHostRuntime" validateOnCreate="true" enablePerformanceCounters="true">

    <services>

    <add type="System.Workflow.Runtime.Hosting.SqlWorkflowPersistenceService, System.Workflow.Runtime, Version=3.0.00000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" connectionString="Data Source=localhost;Initial Catalog=NetFx35Samples_ServiceWorkflowStore;Integrated Security=True;Pooling=False" LoadIntervalSeconds="1" UnLoadOnIdle="true" />

    </services>

    </workflowRuntime>

    </behavior>

    </serviceBehaviors>

    </behaviors>

    </system.serviceModel>

    <system.web>

    <compilation>

    <assemblies>

    <add assembly="System.WorkflowServices, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />

    </assemblies>

    </compilation>

    <identity impersonate="false" />

    </system.web>

    <system.webServer>

    <directoryBrowse enabled="true" />

    </system.webServer>

     

     

      3. Save this file to C:\IISHostForWorkflow.

     

    A couple of things to note regarding this web.config file:

    1. I have added the OOB SQL Persistence service to this example. It is not necessary to do that, so you can remove it if you want. If you do use it and you're using SQLExpress, you need to change the Data Source setting. This example also assumes you've installed and setup the .Net 3.5 Samples located at:

     

    Windows Communication Foundation (WCF), Windows Workflow Foundation (WF) and Windows CardSpace Samples
    http://www.microsoft.com/downloads/details.aspx?FamilyID=2611a6ff-fd2d-4f5b-a672-c002f1c09ccd&DisplayLang=en

     

    1. Notice that the service name points to the same name as the app.config file in our workflow project.
    2. Notice that both endpoint contracts point to the IProductOrder contract.

     

  6. Create a new file for the C:\IISHostForWorkflow directory named OrderProcess.svc. Do this within Visual Studio. I just started by creating a new text file and just renaming it.

     

  7. Paste in the following code:

    <%@ServiceHost language=c# Debug="true" Service="WFToHostInIIS.OrderProcessWF" Factory="System.ServiceModel.Activation.WorkflowServiceHostFactory" %>

     

    Notice how the service that we are referencing here is our workflow.

     

  8. Save the file.
     

  9. Copy your workflow assembly file (from the projects bin\Debug directory) to your virtual directories Bin directory.
     

  10. To make sure that everything is working appropriately, open Internet Explorer and browse to:

    http://localhost/IISWorkflowHost/OrderProcess.svc. 

     

    What you should see is a window similar to what you would see if you browsed to a web service location.

     

     

    Creating a Test Client

     

  11. Create a new Visual C# Windows Console Application named 'ConsoleServiceTest' and place it in the C:\TestWorkflowService directory.
     

  12. Open a command prompt that will allow you to run svcutil.exe. I'm going to just open mine to the Windows SDK directory and run the command:
     

    Svcutil /config:app.config http://localhost/IISWorkflowHost/OrderProcess.svc

    Two files will be created in the directory where you are running svcutil from, an app.config file and an OrderProcessWF.cs file. Add these two files to your console app project. You could also just right click on the project and choose 'Add Service Reference' and find the service.

     

  13. Add a reference to System.ServiceModel and System.Runtime.Serialization to the project.

     

  14. Paste the following code into the Main method in Program.cs:
     

    WFToHostInIIS.OrderInfo oInfo = new WFToHostInIIS.OrderInfo();

    oInfo.FName = "John";

    oInfo.LName = "Doe";

    oInfo.OrderID = "1234";

     

    try

      {

      ProductOrderClient client = new ProductOrderClient();

     client.SubmitOrder(oInfo);

      }

      catch (Exception e)

      {

       Console.WriteLine("Exception: {0}",e.Message);

      }

     

  15. You can step through this using any method you want.