locked
Issues Defining Custom Endpoints for Workflow Service RRS feed

  • Question

  • I have a bit of a conundrum that I've been beating my head against for a day now and I'm hoping that someone out there has an answer. Here's the issue:

    I've got a very simple workflow service that I'm attempting to define using the contract-first method. I've defined a single service contract called IService in a separate assembly called WcfTestServiceContracts.dll. I've added that assembly as a reference in my TestWorkflowService project and imported the contracts so that the WF toolbox contains the receive and send components that support the interface. The IService interface contains a single operation called GetSessionId() which takes no parameters and returns the OperationContext.Current.SessionId as a string.

    The issue I've got is that I'd like to expose some custom endpoints via the services web.config file but I cannot seem to get this to work. I run into 2 different problems.

    Problem #1

    If I define the ConfigurationName property on the workflow to match the name as defined in the web.config file

    <service behaviorConfiguration="Contract1stService" 

             name="TestWorkflowService.Contract1stService">
            <endpoint address="" 

                      binding="basicHttpBinding" 

                      bindingConfiguration="basicHttpBinding" 

                      contract="WcfTestServiceContracts.IService" />

    </service>

    ... then I get expected definition of the endpoints that I have configured in my web.config as shown in IIS Manager/AppFabric. But I also the following error when I try to browse to the xamlx file for the web service.

    So even though it looks like my service implements the contract, IIS and WCF do not see that it does. So this brings me to problem 2.

    Problem #2

    I can correct the IIS/WCF error above by changing the ConfigurationName back to its original value which does not match the fully-qualified service name.

    This then causes the endpoints as viewed in AppFabric (within IIS manager) to revert to the machine defaults. I can then browse to my xamlx file with no problem, HOWEVER... now the default endpoints set up by IIS for http, net.tcp, and net.pipe now no longer match my client proxy bindings that are set in code to match the web.config file and I therefore get the following error:

    So each way I turn I hit an obstacle. My most basic question would by: Why, in Problem #1, is IIS/WCF not recognizing that the IService contract that is listed on the endpoint in IIS Manager is actually implemented by the service.

    Additionally, I've implemented the same service as a straight WCF service and used the same web.config and client side proxy and it works find. It is only when I try to implement this as a contract-first WF service that I hit this problem. All suggestions welcome.


    D. Ferguson

    Tuesday, August 6, 2013 6:11 PM

All replies

  • Here's some simple steps to configuring your workflow service.

    1. in your xamlx file, click on your Workflow Service, and select properties.  The ConfigurationName property of the xamlx must match the service name in web.config:

    ConfigurationName property = MikesWorkflow

        <services>
          <service name="MikesWorkflow">
            <host>
              <baseAddresses>
                <add baseAddress="http://localhost/WorkflowService/MikesWorkflow.xamlx"/>
              </baseAddresses>
            </host>
            <endpoint binding="basicHttpBinding" bindingConfiguration="basic" contract="IMikesWorkflow"/>
          </service>
        </services>

    In your case, it's TestWorkflowService.Contract1stService

    2. Click on your Receive Activity, and your ServiceContractName property there must match your contract in the web config.  Mine is IMikesWorkflow in both places, with no fully qualified namespaces involved.  If you want fullqualified namespace, you need to place them in both.

    Hope that worked.

    Thanks,

    Mike

    • Marked as answer by Pengzhen Song Tuesday, August 13, 2013 11:25 AM
    • Unmarked as answer by dferguson6415 Tuesday, August 13, 2013 4:55 PM
    Thursday, August 8, 2013 5:16 PM
  • Mike,

    Great advice. Thanks.  I changed the ConfigurationName to match the web.config:

    which I'd tried before and the service now works. However, I get the warning indicator saying that my service doesn't implement the contract even though operationally it clearly does. This appears to be caused by me having to include the fully-qualified schema namespace in the contract name as shown below:

    If I don't specify the "{http://profisee.com/demos}" before the contract name then the service doesn't work. If I do specify it, then the service works but I get the warning icon. I can live with the latter but I'd rather get it working properly.

    Thanks!


    D. Ferguson

    Tuesday, August 13, 2013 5:02 PM
  • Can you paste the actual xml from the xamlx file for the WorkflowService(root) node and the Receive node, so i can check out what is going on? And can you paste the system.servicemodel from your web.config.  I couldn't reproduce your problem for the life of me on my machine.

    Thanks,

    Mike

    Tuesday, August 13, 2013 10:59 PM
  • Mike,

    Here's the content of the root element of the of the xamlx file:

    <WorkflowService mc:Ignorable="sap sap2010 sads" p1:TextExpression.Namespaces="{x:Reference __ReferenceID2}" p1:TextExpression.References="{x:Reference __ReferenceID3}" ConfigurationName="TestWorkflowService.Contract1stService" sap2010:ExpressionActivityEditor.ExpressionActivityEditor="C#" sap2010:WorkflowViewState.IdRef="WorkflowService_1" Name="Contract1stService"
     xmlns="http://schemas.microsoft.com/netfx/2009/xaml/servicemodel"
     xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
     xmlns:mca="clr-namespace:Microsoft.CSharp.Activities;assembly=System.Activities"
     xmlns:p="http://profisee.com/demos"
     xmlns:p1="http://schemas.microsoft.com/netfx/2009/xaml/activities"
     xmlns:sads="http://schemas.microsoft.com/netfx/2010/xaml/activities/debugger"
     xmlns:sap="http://schemas.microsoft.com/netfx/2009/xaml/activities/presentation"
     xmlns:sap2010="http://schemas.microsoft.com/netfx/2010/xaml/activities/presentation"
     xmlns:scg="clr-namespace:System.Collections.Generic;assembly=mscorlib"
     xmlns:sco="clr-namespace:System.Collections.ObjectModel;assembly=mscorlib"
     xmlns:w="clr-namespace:WcfTestServiceContracts;assembly=WcfTestServiceContracts"
     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">

    and the related receive element:

          <Receive x:Name="__ReferenceID0" Action="http://profisee.com/demos/IService/GetSessionId" CanCreateInstance="True" DisplayName="GetSessionId_Receive" sap2010:WorkflowViewState.IdRef="Receive_2" OperationName="GetSessionId" ServiceContractName="p:WcfTestServiceContracts.IService">
            <Receive.CorrelationInitializers>
              <RequestReplyCorrelationInitializer>
                <RequestReplyCorrelationInitializer.CorrelationHandle>
                  <p1:InArgument x:TypeArguments="CorrelationHandle">
                    <p1:VariableValue x:TypeArguments="CorrelationHandle">
                      <p1:VariableValue.Variable>
                        <p1:Variable x:TypeArguments="CorrelationHandle" x:Name="__ReferenceID1" Name="__handle" />
                      </p1:VariableValue.Variable>
                    </p1:VariableValue>
                  </p1:InArgument>
                </RequestReplyCorrelationInitializer.CorrelationHandle>
              </RequestReplyCorrelationInitializer>
            </Receive.CorrelationInitializers>
            <ReceiveParametersContent />
          </Receive>
    

    And finally, if it's helpful, here's the contract as defined in the contracts assembly:

    namespace WcfTestServiceContracts
    {
        // NOTE: You can use the "Rename" command on the "Refactor" menu to change the interface name "IService1" in both code and config file together.
        [ServiceContract(Namespace="http://profisee.com/demos",SessionMode=SessionMode.Allowed)]
        public interface IService
        {
            [OperationContract]
            string GetSessionId();
        }
    
    }
    Thanks for the guidance Mike.

    D. Ferguson

    Wednesday, August 14, 2013 11:05 AM
  • Mike,

    Sorry, I forgot the web.config bits in my last post. Here they are:

    	<system.serviceModel>
    		<behaviors>
    			<serviceBehaviors>
    				<behavior>
    					<!-- To avoid disclosing metadata information, set the values below to false before deployment -->
    					<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
    					<!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
    					<serviceDebug includeExceptionDetailInFaults="false"/>
    				</behavior>
    				<behavior name="Contract1stService">
    					<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
    					<serviceDebug includeExceptionDetailInFaults="true"/>
    					<dataContractSerializer maxItemsInObjectGraph="2147483647"/>
    				</behavior>
    			</serviceBehaviors>
    		</behaviors>
    		<services>
    			<service behaviorConfiguration="Contract1stService" name="TestWorkflowService.Contract1stService">
    				<endpoint address="" binding="wsHttpBinding" bindingConfiguration="wsHttpBinding" name="wsHttpBinding" contract="WcfTestServiceContracts.IService"/>
            <endpoint address="basic" binding="basicHttpBinding" bindingConfiguration="basicHttpBinding" contract="WcfTestServiceContracts.IService" />
            <endpoint address="np" binding="netNamedPipeBinding" bindingConfiguration="netNamedPipeBinding" name="netNamedPipeBinding" contract="WcfTestServiceContracts.IService"/>
    				<endpoint address="tcp" binding="netTcpBinding" bindingConfiguration="netTcpBinding" name="netTcpBinding" contract="WcfTestServiceContracts.IService" />
    				<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
    			</service>
    		</services>
    		<bindings>
    			<basicHttpBinding>
    				<binding name="basicHttpBinding" closeTimeout="24.20:31:23.6470000" openTimeout="24.20:31:23.6470000" receiveTimeout="24.20:31:23.6470000" sendTimeout="24.20:31:23.6470000" maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647">
    					<readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
    					<security mode="TransportCredentialOnly">
    						<transport clientCredentialType="Windows" />
    					</security>
    				</binding>
    			</basicHttpBinding>
    			<netNamedPipeBinding>
    				<binding name="netNamedPipeBinding" closeTimeout="00:1:00" openTimeout="00:01:00" receiveTimeout="24.20:31:23.6470000" sendTimeout="24.20:31:23.6470000" transactionFlow="false" transferMode="Buffered" transactionProtocol="OleTransactions" hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="2147483647" maxBufferSize="2147483647" maxConnections="1024" maxReceivedMessageSize="2147483647">
    					<readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647"/>
    					<security mode="Transport">
    						<transport protectionLevel="None"/>
    					</security>
    				</binding>
    			</netNamedPipeBinding>
    			<netTcpBinding>
    				<binding name="netTcpBinding" closeTimeout="00:1:00" openTimeout="00:01:00" receiveTimeout="24.20:31:23.6470000" sendTimeout="24.20:31:23.6470000" transactionFlow="false" transferMode="Buffered" transactionProtocol="OleTransactions" hostNameComparisonMode="StrongWildcard" listenBacklog="2147483647" maxBufferPoolSize="2147483647" maxBufferSize="2147483647" maxConnections="1024" maxReceivedMessageSize="2147483647">
    					<readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
    					<reliableSession ordered="true" inactivityTimeout="00:30:00" enabled="false" />
    					<security mode="Transport">
    						<transport clientCredentialType="Windows" protectionLevel="None"/>
    					</security>
    				</binding>
    			</netTcpBinding>
    			<wsHttpBinding>
    				<binding name="wsHttpBinding" closeTimeout="24.20:31:23.6470000" openTimeout="24.20:31:23.6470000" receiveTimeout="24.20:31:23.6470000" sendTimeout="24.20:31:23.6470000" maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647">
    					<readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647"/>
    					<reliableSession inactivityTimeout="24.20:31:23.6470000"/>
    					<security mode="Message">
    						<message clientCredentialType="Windows"/>
    					</security>
    				</binding>
    			</wsHttpBinding>
    		</bindings>
    		<!--protocolMapping>
    			<add binding="basicHttpsBinding" scheme="https" />
    		</protocolMapping-->
    		<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
    	</system.serviceModel>
    

    Thanks.


    D. Ferguson

    Wednesday, August 14, 2013 11:09 AM
  • Hey Ferg,

    So you don't actually need to define the contract in code, IService.  WF auto-creates the interface based on the ServiceContractName of the Receive Activity.  I think the warning is because you have defined the interface twice.  Take out the IService interface from code, and you should be all good.  The fact that you have IService as your ServiceContractName is enough, and the interface will be inferred by WF.

    Thanks,

    Mike

    Wednesday, August 14, 2013 10:03 PM
  • Wow... LOL! 'Ferg'... haven't been called that since high school. And then it was Fergy. Takes me back to the good ol' days when I didn't have to worry about workflows and deadlines!

    Anyway... isn't the whole point of WF's Contract First model to be able to define your contracts in code and then import them in to generate your specific ReceiveAndSendReply activities? Yes, I could do it workflow first but from an SOP perspective, we try to develop our contracts (service, message, operation, and data) first and place them into a separate assembly that can be included by clients of the service.

    Maybe I'm missing something. Wouldn't be the first time!

    Regards!


    D. Ferguson

    Thursday, August 15, 2013 2:25 AM
  • Ha...Ferg.  I see, if it's for your SOP's, then i understand.  You can always just add a service reference for your workflows, or generate the client proxy from the wsdl, but if you have a certain pattern for your job, then you can do contract first.  I have recreated your service from this post on my local machine, and still can't reproduce your warning error even having created the contract first and imported the service contract into the project.  Unfortunately, i don't think i can be of much help from here.

    Mike

    Thursday, August 15, 2013 4:26 PM
  • Hi Mike,

    Would you mind just zipping up your VS solution and e-mail it (david.ferguson@profisee.com) to me or upload it you a public SkyDrive folder? I can look for any differences then. That would be helpful.

    I very much appreciate your time and advice. You've been most helpful!

    Regards,


    D. Ferguson

    Thursday, August 15, 2013 5:12 PM
  • Hi Mike,

    Circling back around to the differences between your workflow project and mine (sorry for the delay) – here’s what I’ve found:

    I was able to get the same results as you by doing the following: I removed the reference to the external assembly that contains my contracts and just pulled the service contract directly into the workflow service project. This got rid of the warning indicator and the service worked fine but… that’s not the way we structure our contracts. They need to be in a separate assembly.

    So I then removed the service contract back out of the workflow service project and reintroduced the contracts assembly. And mysteriously the warning did not come back. But I wasn’t satisfied with that so I looked a bit further and realized that what was different than before was that the collection of ImplementedContracts in the properties of my workflow service was empty – I had not set the value back the way it was.

    So when I actually set the implemented contractsto IService specified in the WcfTestServiceContracts assembly, low and behold the warning came back.

    So then I went back to your project and noted that you had not specified the ImplementedContracts property so I went ahead and added there and, yes, low and behold, the same warning appears on your project.

    So, WF/WCF doesn’t seem to care that the ImplementedContracts is not set. The service still works. This is bothersome but it won’t keep me from moving forward.

    Does this look like a bug to you? I’m suspicious.


    D. Ferguson

    Wednesday, August 21, 2013 6:58 PM