Ask a questionAsk a question
 

AnswerSteps for integrating WCF and WF

  • Friday, May 11, 2007 9:33 AMramkasarla Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    hi

    i am in learning stage of WCF and WF, I want to know that

     

    1. while integrating wheather WCF hosts the WF or WF hosts the WCF.

    2. Is there any detailed document about how to  integrate  WCF and WF

    3. what are the necessary steps to follow for integration of WCF and WF.

     

     

    thanks,

Answers

  • Friday, May 11, 2007 11:15 AMAlan Smith MVPMVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

    Hi,

     

    1. Ive looked at this a little with the .net 3 release. As a proof of concept, i built a console app that hosted both the WCF services, and the WF workflow runtime. I implemented code in the WCF services to interace with the WF runtime, and create and manage the workflows.

     

    2. I've not seen any good docs for how to do this with .net 3, but there's a cople of good blog articles on doing this with .net 3.5 beta 1 and Orcas posted by Guy Burstein:

    http://blogs.microsoft.co.il/blogs/bursteg/

     

    3. I'd recomend taking a look at beta 1 of Orcas if it's possible in your project. It seems that the new version provides much better OOB integration.

     

    Regards,

     

    Alan

     

     

All Replies

  • Friday, May 11, 2007 11:15 AMAlan Smith MVPMVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     Answer

    Hi,

     

    1. Ive looked at this a little with the .net 3 release. As a proof of concept, i built a console app that hosted both the WCF services, and the WF workflow runtime. I implemented code in the WCF services to interace with the WF runtime, and create and manage the workflows.

     

    2. I've not seen any good docs for how to do this with .net 3, but there's a cople of good blog articles on doing this with .net 3.5 beta 1 and Orcas posted by Guy Burstein:

    http://blogs.microsoft.co.il/blogs/bursteg/

     

    3. I'd recomend taking a look at beta 1 of Orcas if it's possible in your project. It seems that the new version provides much better OOB integration.

     

    Regards,

     

    Alan

     

     

  • Saturday, May 12, 2007 1:21 AMJon FlandersMVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     
    It is certainly possible to integrate WF and WCF today using custom ServiceHosts and invoking workflows directly.  I'll be doing a session at TechED US on that topic - http://www.masteringbiztalk.com/blogs/jon/PermaLink,guid,116a206e-db52-41de-afb4-17a6e022e093.aspx  and hopefully around that time or soon after that a sample I've built for MS will be made public that relates to WF autohosting in WCF using .NET 3.0 and not Orcas.
  • Tuesday, May 15, 2007 4:33 AMRoman KissMVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    ramkasarla,

     

    - Definitely, you should look at the OrcasBeta1 features, that's the primary goal of the .netfx 3.5 - connected systems using these technologies.

     

    - For the current .netfx 3.0 version, the WCF/WF hosting and raising the workflow HEEA event can be really simply like is shown in the following code snippet, service1.svc file: 

    Code Snippet

    <%@ServiceHost
        Language=C#
        Debug="true"
        Service="ServiceTest1"
        Factory="RKiss.WorkflowEventService.ServiceHostActivation, WorkflowEventService" %>

     

    // .netfx3             
     using System;
     using System.ServiceModel;
     using System.Workflow.Activities;

     

    // WCF/WF integration
     using RKiss.WorkflowEventService;

     

    // application specific
     using WorkflowLibrary1;

                  
     [ServiceContract]
     [ServiceBehavior(IncludeExceptionDetailInFaults = true)]
     public class ServiceTest1
     {
        [OperationContract(IsOneWay = true)]
        [WorkflowFireEvent(EventType = typeof(IWorkflowEvent))]
        public void Approve(object sender, ExternalDataEventArgs e)
        {
           throw new NotImplementedException();
        }
     }

     

    where, the Factory attribute of the @ServiceHost enables to integrate WCF hosting with WorkflowRuntime. The following example shows its implementation:

    Code Snippet

    using System;

    using System.Diagnostics;

    using System.Collections.Generic;

    using System.Text;

    using System.Workflow.Runtime;

    using System.ServiceModel;

    using System.ServiceModel.Activation;

    using System.Reflection;

    namespace ConsoleApplication1

    {

      public class WorkflowRuntime2 : WorkflowRuntime, IExtension<ServiceHostBase>

      {

        public WorkflowRuntime2() : this("WorkflowRuntimeConfig") { }

        public WorkflowRuntime2(string configSection) : base(configSection) { }

        public void Attach(ServiceHostBase owner)

        {

          base.StartRuntime();

        }

        public void Detach(ServiceHostBase owner)

        {

          if (base.IsStarted)

           base.StopRuntime();

        }

      }

     

      public class ServiceHostActivation : ServiceHostFactoryBase

      {

        public override ServiceHostBase CreateServiceHost(string constructorString, params Uri[] baseAddresses)

        {

         return Create(constructorString, baseAddresses);

        }

        public static ServiceHost Create(string constructorString, params Uri[] baseAddresses)

        {

          // find the type of the service

          Type serviceType = Type.GetType(constructorString, false);

          foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())

          {

            serviceType = assembly.GetType(constructorString, false);

            if (serviceType != null)

            {

              break;

            }

          }

          // validate type

          if (serviceType == null)

           throw new ArgumentException("Can't find the assembly to create a service type");

          return Create(serviceType, baseAddresses);

        }

        public static ServiceHost Create(Type serviceType, params Uri[] baseAddresses)

        {

          // create wcf host

          ServiceHost host = new ServiceHost(serviceType, baseAddresses);

          host.Closing += new EventHandler(host_Closing);

     

          // Service extension for WF

          WorkflowRuntime2 workflowRuntime = new WorkflowRuntime2();

     

          // Add the Extension to the ServiceHost collection

          host.Extensions.Add(workflowRuntime);

          return host;

        }

        private static void host_Closing(object sender, EventArgs e)

        {

          WorkflowRuntime wr = (sender as ServiceHost).Extensions.Find<WorkflowRuntime>();

          if (wr != null)

          {

            wr.Dispose();

          }

        }

      }

    }

     

    The above servive.svc file also integrates a service operation contract with a workflow contract using the custom WorkflowFireEvent attribute described in the article Fire WorkflowEvent from WCF. This attribute enables to forward the wcf message to the workflow queue.

     

    The service.svc needs to have a web.config file, where are located all metadata for this connected systems:

    Code Snippet

     

    <configuration>

      <configSections>

        <section name="WorkflowRuntimeConfig" type="System.Workflow.Runtime.Configuration.WorkflowRuntimeSection, System.Workflow.Runtime, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>

        <section name ="LocalServicesConfig" type ="System.Workflow.Activities.ExternalDataExchangeServiceSection, System.Workflow.Activities, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>

      </configSections>

     

      <WorkflowRuntimeConfig >

        <Services>

          <add type="System.Workflow.Activities.ExternalDataExchangeService, System.Workflow.Activities, Version=3.0.00000.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" ConfigurationSection="LocalServicesConfig" />

          <!--<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.SqlWorkflowPersistenceService, System.Workflow.Runtime, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" UnloadOnIdle="true" LoadIntervalSeconds="10" ConnectionString="Initial Catalog=PersistenceStore;Data Source=localhost\SQLEXPRESS;Integrated Security=SSPI;"/>

          <add type="System.Workflow.Runtime.Tracking.SqlTrackingService, System.Workflow.Runtime, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" UnloadOnIdle="true" LoadIntervalSeconds="10" ConnectionString="Initial Catalog=TrackingStore;Data Source=localhost\SQLEXPRESS;Integrated Security=SSPI;" />

        </Services>

      </WorkflowRuntimeConfig>

     

      <LocalServicesConfig>

        <Services>

        </Services>

      </LocalServicesConfig>

     

     <system.serviceModel>

      <services>

        <service name="ServiceTest1" behaviorConfiguration="MyServiceTypeBehaviors">

          <endpoint address="mex" contract="IMetadataExchange" binding="mexHttpBinding"/>

          <endpoint address="" contract="ServiceTest1" binding="wsHttpBinding"/>

        </service>

      </services>

      <bindings>

        <wsHttpBinding>

        </wsHttpBinding>

      </bindings>

      <behaviors>

        <serviceBehaviors>

          <behavior name="MyServiceTypeBehaviors" >

             <serviceMetadata httpGetEnabled="true" />

          </behavior>

        </serviceBehaviors>

      </behaviors>

     </system.serviceModel>

     

     <system.web>

      <compilation debug="true">

        <assemblies>

          <add assembly="System.Workflow.Activities,

             Version=3.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>

        </assemblies>

      </compilation>

     </system.web>

    </configuration>

     

     

    Thanks

     

    Roman  

     

  • Monday, May 21, 2007 2:49 PMkfrost Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    RKiss,

     

    Do you have a solution for this posted somewhere?  Or does anybody have an example of the ExpenseReporting V2 service where they've modified it to work with WCF being hosted via IIS?

     

    Thanks.

  • Monday, May 21, 2007 2:51 PMMarcel de VriesMVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    For easy WCF/WF integration you can also use the Custom activities I published on CodePlex that provide an implementation like the ASMX input and output activities currently in the product. This way you can pick an interface and method you want to implement in your workflow and add a corresponding output activity when you have the results of the service method.

     

    The code will handle workflow instantiation, correlation between input and output and also plugs into the correct workflow runtime you are hosting.

    Using these activities will keep you from writing plumbing code yourself and keep your code as clean as possible so you can easily migrate to ORCAS features once they ship. 

    You can find the docs and samples as http://www.codeplex.com/WCFWorkflow

     

    Regards,

    Marcel

     

  • Monday, May 21, 2007 3:16 PMkfrost Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    Marcel,

     

    Thanks for the post.  Questions.

    1)  Where is the source to these activities?  Not sure about anybody else but not a good practice to implement components in production posted on the web without the source to be able to troubleshoot and fix in house if necessary?  I've been looking but have yet to run across the bits behind the activities.  Are these available.

     

    2)  I would still like to know the process of doing this behind the scenes.  MS should put out more real world examples and hosting WCF via IIS seems like a theme of interest.  The Expense Reporting V2 example is a pretty decent sample but like most others relies on a console app for hosting which I would contend that not many writing real world apps is going to rely on?  I'm trying to modify pick through snippets here and there to try and figure out how to configure the web.config etc to get a version of the ExpenseReporting sample to work using IIS as a host?

     

    Has anybody already done this and could post?  Or anybody with experience of integrating WF with WCF possibly be willing to look at the expense reporting example and post the mods necessary.  RKiss's post seems to be on the right track but seems to be missing some relavent info to completely get things work.

     

    Thanks.

  • Monday, May 21, 2007 6:04 PMRoman KissMVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    kfrost,

     

    The following steps are WCF and WF basic integration:

     

    1. Hosting: - WCF and WF hosting is done in the .svc file using the Factory attribute. This is a custom hosting based on the ServiceHostFactoryBase class and its source code can be downloaded from Fire WorkflowEvent from WCF or from this thread - see my previously post.

     

    Code Snippet, service1.svc

    <%@ServiceHost
        Language=C#
        Debug="true"
        Service="ServiceTest1"

       CodeBehind="~/App_Code/Service1.cs"
        Factory="RKiss.WorkflowEventService.ServiceHostActivation, WorkflowEventService" %>

     

     

    2. Invoking Worfklow in the codeBehind using a Worfklow Local Service:

     

     

    Code Snippet

    [ServiceContract]
     public class ServiceTest1 : IMyInterface
     {
        [OperationContract]
        public void Approve(object sender, ExternalDataEventArgs e)
        {
            // Get reference to the WorkflowRuntime
            WorkflowRuntime workflowRuntime = OperationContext.Current.Host.Extensions.Find<WorkflowRuntime>();

          

          // raise event on the Worfklow Local Service

          // todo:

     

       }
     }

     

     

    Thanks

     

    Roman

     

     

     

  • Monday, May 21, 2007 6:22 PMkfrost Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    Hey Roman,

     

    Thanks for the post but not using code behind in the svc file.  I would imagine anybody writing a production app, the code for the service is in a separate assembly. 

     

    I've been trying to piece through your example from your website and I've found links on the Factory setting at.

    Thread on IIS hosting

    http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=1588040&SiteID=1

    @ServiceHost parameters in a svc file. 

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

     

    Even still trying to piece meal incomplete code snippets from forums still not happening, getting an error that it can't load specific type.  You use ServiceHostFactoryBase but Microsoft examples use just ServiceHostFactory.  Not sure why the difference but again, it's fighting through trying to get something to work.

     

    My comments were in hopes that Microsoft was monitoring these threads.  It would be nice to have examples such as expenseReporting that mimicked real world.  The app and concepts itself are good, but the host as with most, a console app was used which is fine as a for an example that could be followed for hosting via a windows service but Not very helpful when utilizing IIS. 

     

    Sure will get through it eventually but it would be nice to have complete relevant examples from Microsoft to cut out the "get through it eventually" portions everytime trying to learn a product from them.

     

    Thanks again for the posts

  • Monday, May 21, 2007 7:52 PMRoman KissMVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    kfrost,

     

    - To perform IIS/WCF hosting  via @ServiceHost directive, the type on the optional attribute Factory must be derived from ServiceHostFactoryBase class in order to call its abstract method:

    public abstract ServiceHostBase CreateServiceHost(string constructorString, Uri[] baseAddresses);
    where the constructorString parameter is a name of the service type. The method has a responsibility to convert this string to the actually type and returning the ServiceHost reference:
    return new ServiceHost(serviceType, baseAddresses);
    Additionally, in my custom ServiceHostActivation class, the WorkflowRuntime is attached to the service host.
    I think, the problem can be related to the assembly reference or endpoint configuration. Check the assemblies in the /bin folder.
     
    Thanks
    Roman
     
  • Tuesday, May 22, 2007 7:15 AMMarcel de VriesMVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    well I posted everything on codeplex and there you can download the latest changeset that contains all the sources.(including a sample applciation and step by step guide)

    See following link to the latest change set: http://www.codeplex.com/WCFWorkflow/SourceControl/DownloadSourceCode.aspx?changeSetId=2184

     

    Also the installer intalls the sources of the activities in the program files/beyondDotNet folder. There you will also find the step by step guide.

     

    Hope that helps,

    Regards,

    Marcel

  • Tuesday, May 29, 2007 9:48 PMkfrost Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    Ok, still plugging away at this thing.  MS, if you are watching would be tremendously nice if you could post a complete example of this working.  Everybody else that has posted, thanks but it's a pain trying to sort through others half posts trying to piece together.  Roman, tried to add an IIS host to your example but keep running into nagging glitches you get when you haven't done something before and you have an hour or two here and there per week to try and make sense of a bunch of piece meal.

     

    Looking at it all today, I'm back to the expense reporting example to possibly get it to work.  It appears that everything in the wcfextensions file is needed.  But when you want utilize IIS as a host, you need to add the factory class as roman has pointed out.  What I'm experimenting with now is just adding his code at the bottom of his file like this.

     

      public class ServiceHostActivation : ServiceHostFactoryBase
            {
                public override ServiceHostBase CreateServiceHost(string constructorString, params Uri[] baseAddresses)
                {
                    return Create(constructorString, baseAddresses);
                }

                public static ServiceHost Create(string constructorString, params Uri[] baseAddresses)
                {
                    // find the type of the service
                    Type serviceType = Type.GetType(constructorString, false);

                    foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
                    {
                        serviceType = assembly.GetType(constructorString, false);
                        if (serviceType != null)
                        {
                            break;
                        }
                    }

                    // validate type
                    if (serviceType == null)
                    {
                        throw new ArgumentException("Can't find the assembly to create a service type");
                    }
                    return Create(serviceType, baseAddresses);
                }

                public static ServiceHost Create(Type serviceType, params Uri[] baseAddresses)
                {
                    // create wcf host
                    ServiceHost host = new ServiceHost(serviceType, baseAddresses);
                    host.Closing += new EventHandler(host_Closing);

                    // Service extension for WF Supply Web.Config section with parameters
                    WfWcfExtension workflowRuntime = new WfWcfExtension("WorkflowRuntimeConfig");

                    // Add the Extension to the ServiceHost collection
                    host.Extensions.Add(workflowRuntime);

                    return host;

                }

                private static void host_Closing(object sender, EventArgs e)
                {
                    WorkflowRuntime wr = (sender as ServiceHost).Extensions.Find<WorkflowRuntime>();
                    if (wr != null)
                    {
                        wr.Dispose();
                    }
                }
            }

     

    Then the svc file looks as such:  (To do this you'll need to setup a IIS website and add a wcf service file)

    <%@ ServiceHost Language="C#" Debug="true"
        Service="ExpenseServices.ExpenseService, ExpenseService"
        Factory="WcfExtensions.ServiceHostActivation, WcfExtensions" %>

     

    With the web.config looking like this so far.  (May be some kinks with it that I haven't worked out.)

    <configuration>
      <configSections>
        <section name="WorkflowRuntimeConfig" type="System.Workflow.Runtime.Configuration.WorkflowRuntimeSection, System.Workflow.Runtime, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
      </configSections>

      <WorkflowRuntimeConfig>
        <Services>
          <add type="System.Workflow.Runtime.Hosting.SqlWorkflowPersistenceService, System.Workflow.Runtime, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" ConnectionString="Initial Catalog=WorkflowPersistence;Data Source=sc430x64-01;Integrated Security=SSPI;"/>
          <add type="System.Workflow.Runtime.Tracking.SqlTrackingService, System.Workflow.Runtime, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" ConnectionString="Initial Catalog=WorkflowTracking;Data Source=sc430x64-01;Integrated Security=SSPI;" IsTransactional="false" UseDefaultProfile="true" TrackXomlDocument="True"/>
        </Services>
      </WorkflowRuntimeConfig>
      <system.serviceModel>
        <services>
          <!-- Before deployment, you should remove the returnFaults behavior configuration to avoid disclosing information in exception messages -->
          <service name="ExpenseServices.ExpenseService" behaviorConfiguration="ExpenseServiceBehavior">
            <endpoint contract="ExpenseContracts.IExpenseService" binding="wsHttpBinding"/>
          </service>
        </services>
        <behaviors>
          <serviceBehaviors>
            <behavior name="ExpenseServiceBehavior">
              <serviceDebug includeExceptionDetailInFaults="true"/>
              <serviceMetadata httpGetEnabled="true" />
            </behavior>
          </serviceBehaviors>
        </behaviors>
      </system.serviceModel>
     <appSettings/>
     <connectionStrings/>

     

    Trying to get everything to compile and then try to change the Expense and Manager App to utilize the IIS svc and see where it gets me.

     

    I'll post results when I get finished

     

     

  • Wednesday, May 30, 2007 12:29 AMkfrost Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    Roman,

     

    With Expense Reporting with the mods above I'm back to the same problem I had trying to utilize your code in another example.  I'm not understanding this intent for this section of code.

     

    foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
                {
                    serviceType = assembly.GetType(constructorString, false);
                    if (serviceType != null)
                    {
                        break;
                    }
                }

                // validate type
                if (serviceType == null)
                {
                    throw new ArgumentException("Can't find the assembly to create a service type");
                }

     

    there are plenty of assemblies but all equate to null and throws an exception.  To add to this, if I comment this section of my code then things appear to work.  Roman, could you please ellobrate on this section and by chance have any comments as to whether it's needed or not?

     

    Thanks.

     

    Can't find the assembly to create a service type

    Description: An unhandled exception occurred during the execution of the current web request. Please review the stack trace for more information about the error and where it originated in the code.

    Exception Details: System.ArgumentException: Can't find the assembly to create a service type

    Source Error:

    Line 113:            if (serviceType == null)
    Line 114:            {
    Line 115:                throw new ArgumentException("Can't find the assembly to create a service type");
    Line 116:            }
    Line 117:            return Create(serviceType, baseAddresses);

    Source File: C:\Data\TFS\corp-tfs-01\Template\ExpenseReporting\ExpenseReporting\WCFExtensions\WfWcfExtension.cs    Line: 115

    Stack Trace:

  • Wednesday, May 30, 2007 1:10 AMRoman KissMVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    kfrost,

     

    - all assemblies must be in the \bin folder or GAC.

     

    - correct my code by the following code snippet.

    Code Snippet - correction
      // find the type of the service

     Type serviceType = Type.GetType(constructorString, false);

     if(serviceType == null)

     {

       foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())

       {

          serviceType = assembly.GetType(constructorString, false);

          if (serviceType != null)

          {

             break;

           }

       }

     }

     

    Thanks

     

    Roman

     

  • Wednesday, May 30, 2007 1:22 AMkfrost Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    Yes, but why.  I wouldn't really care to register the assemblies in the GAC in this case.  That wouldn't seem impratical.  Plus if I just remove that code, the call into the factory has the correct config string and everything and appears to work if I comment the code out you posted.

     

    What would the code in question here buy me and if it were something important is there another way because registering the assemblies in this case in the GAC would probably not be an option.

     

    Thanks.

     

  • Wednesday, May 30, 2007 3:14 AMRoman KissMVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    kfrost,

     

    - that's the option by IIS (~/bin or GAC)

     

    - have a look at the @Assembly directive

     

    - in order to configure WCF service, the Service attribute (aka constructorString) must be converted to the service type for creating a ServiceHost.

      In your case: Service="ExpenseServices.ExpenseServicewill find a service type through walking all loaded assemblies.

     

    Thanks

     

    Roman

     

     

  • Wednesday, May 30, 2007 11:11 AMkfrost Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    Ah, ok I think I'm a little more clear on your thought process.  The web project does have a reference to the ExpenseService project and the dll for this assembly is created in the Bin folder of the IIS project.  Earlier I was having a problem indicating the method for assembly ExpenseService could not be loaded.  In the error it pointed out where the assembly had to be located and bin file was an option as you mentioned.  (My problem there was I erroneous had the serviceHostActivation class nested inside the WfWcfExtension class in the WcfWfExtensions namespace.)

     

    Past that, now when I attach the debugger to aspnetwp.exe and I step into it.  When the Create class is fired from the CreateServiceHost constructor, constructor string is set to ExpenseServices.ExpenseService and serviceType does have a valid value.  As I mention, if I comment out your foreach loop, serviceType upon the inital call is set to a valid value and everything appears to work.  (I'll know for sure when I have a chance to change the host the apps are utilizing in the sample.)

     

    So with that said, i kind of see what was being done with the foreach loop but confused as to why it's needed because what I see at present, the serviceType is getting set to the correct Type in the first call.

     

                // find the type of the service
                Type serviceType = Type.GetType(constructorString, false);

    The foreach loop appears to be trying to find the exact same thing.  Shouldn't there be a conditional statement surrounding the foreach in the example to where if serviceType is not null as a result of:

    Type serviceType = Type.GetType(constructorString, false);

     

    As I talk about it an understand it now, it seems as if this statement would populate serviceType correctly if you add a reference to the correct assembly and it's present in the bin file.  (As I'm witnessing.)   If not present here, then the foreach loop would be used to go out and try to find it in the gac as you mentioned?

     

     

  • Wednesday, May 30, 2007 4:19 PMRoman KissMVPUsers MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    kfrost,

     

    - that's correct, I apologize for the following bug:

    Code Snippet - correction

     // find the type of the service

     Type serviceType = Type.GetType(constructorString, false);

     if(serviceType == null)

     {

       foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())

       {

          serviceType = assembly.GetType(constructorString, false);

          if (serviceType != null)

          {

             break;

           }

       }

     }

     

    Btw., in the .Net Framework 3.5 (Orcas/B1), the Service attribute can have also a name of the xoml file - see the System.ServiceModel.Activation.WorkflowServiceHostFactory class.

     

    Thanks

     

    Roman

  • Thursday, May 31, 2007 11:51 AMkfrost Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    Thanks Roman,

     

    I think I've got everything setup to where the Workflow runtime is loading and have it referencing the local service.  Now I'm into the bindings trying to mimick what expense reporting has in a console app.

     

    I used wcf to create extra bindings that point to IExpenseManager and IExpenseServiceClient but when I try to open the svc I get the error.

     

    A binding instance has already been associated to listen URI 'http://clt-wxp-11.clt.pcts.com/ExpenseReporting/ExpenseService.svc'. If two endpoints want to share the same ListenUri, they must also share the same binding object instance. The two conflicting endpoints were either specified in AddServiceEndpoint() calls, in a config file, or a combination of AddServiceEndpoint() and config.

    I've tried adding a new service in the WCF editor but can't because ExpenseService is the only service available in the projects?

     

    I'm wondering if I have to create 2 more svc files, one for the client and one for the manager to get this to work and then set the contracts to the appropriate interfaces?

     

    Any thoughts?

     

  • Thursday, May 31, 2007 1:31 PMkfrost Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    Ok, per this article:

    http://msdn.microsoft.com/msdnmag/issues/07/06/ServiceStation/

     

    Appears you have to add relative addresses for each additional endpoint bound to a service.  I did this and ran the add service reference from the ManagerApplication and notice in the app.config file it does add the endpoints as ExpenseReporting/ExpenseService.svc/client

     

     

  • Friday, June 01, 2007 8:52 PMkfrost Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    Ok, hit a snag.  Utilizing the expense reporting and other examples thought I had everything up and running.  However, I found the following is generating an exception.

     

    private void SetUpWorkflowEnvironment()

    {

    if (arithmeticLocalService == null)

    {

    WfWcfExtension extension =

    OperationContext.Current.Host.Extensions.Find<WfWcfExtension>();

    workflowRuntime = extension.WorkflowRuntime;

    // We will try and fetch the instance from the active WorkflowRuntime's

    // ExtenalDataExchangeService and if it currently does not have an

    // activated instance, we will create a new instance and add it.

    ExternalDataExchangeService dataExchangeService =

    workflowRuntime.GetService<ExternalDataExchangeService>();

    arithmeticLocalService =

    (ArithmeticLocalService)dataExchangeService.GetService(typeof(ArithmeticLocalService));

    if (arithmeticLocalService == null)

    {

    arithmeticLocalService = new ArithmeticLocalService(workflowRuntime);

    dataExchangeService.AddService(arithmeticLocalService);

    }

     

    // Events are then wired up to our local event handlers

    //arithmeticLocalService.ManagerApprovalRequested +=

    // new EventHandler<ManagerApprovalRequestedEventArgs>(arithmeticLocalService_ManagerApprovalRequested);

    //arithmeticLocalService.ManagerReviewed +=

    // new EventHandler<ManagerReviewedEventArgs>(arithmeticLocalService_ManagerReviewedOrTimeout);

    }

    }

     

    workflowRuntime.GetService<ExternalDataExchangeService>();

    Kind of puzzled how that call is causing the exception, "More than one Runtime Service exists of Type: ExternalDataExchangeService"

     

    This is supposed to be getting registered in the web.config.  Plus that statement to me means it's going out and trying to look for it, not create it so the error doesn't make sense.

     

    Any ideas?

     

  • Monday, June 04, 2007 7:09 PMkfrost Users MedalsUsers MedalsUsers MedalsUsers MedalsUsers Medals
     

    The above exception turned out to be valid.  To get around the problem above I commented out the add section in the web.config.  I found a piece of code in the WcfExtensions code i borrowed from the expenseReporting example and commented it out to resolve the problem.

     

    Now to the next dilemma.  I have everything running and working but I see some potential pitfals.

     

    First I had to utilize an AutoResetEvent and implement waitOne after the call to start the workflow Instance.

     

    private AutoResetEvent _waitHandle = new AutoResetEvent(false);

     

    SetUpWorkflowEnvironment();

    // If client supplies instanceId to an existing or persisted workflow,

    // use it, if not create a new one

    if (instanceId != Guid.Empty)

    {

    instanceId = clientInstanceId;

    }

    else

    {

    instanceId = Guid.NewGuid();

    }

    Assembly asm = Assembly.Load("ArithmeticWF");

    Type workflowType = asm.GetType("ArithmeticWF.DivideWorkflow");

    // Need to populate Dictionary here to pass params in.

    Dictionary<string, object> wfParams = new Dictionary<string, object>();

    // Params of dictionary must match the object name in the workflow

    wfParams.Add("ProductObj", product);

    WorkflowInstance workflowInstance = workflowRuntime.CreateWorkflow(workflowType, wfParams, instanceId);

    workflowInstance.Start();

    // This must be here to give the workflow time to run and it uses the workflow_completed event to populate

    // result. if you remove this, returnValue will return back as 0.

    _waitHandle.WaitOne();

     

    // Return value was set in the workflow_completed event using e.OutputParameters["DivideWorkflow_ReturnVal"]

    response.ReturnVal = product.Val1 / this.ReturnValue;

     

     

    This works but the problem is, that the waitOne() causes the workflowRuntime to block until the workflow is finished.  So you take a one workflow that takes 15 seconds to finish and then you start handling hundreds to thousands of simultaneous client requests, then we're moving towards performance problems.

     

    I'm assuming the next thing I have to figure out is in this environment how to spin up new workflowRuntime's when there isn't one available to handle a client request?  Correct?  Any input to this thread pertaining to this would be appreciated.

     

    Thanks.