Wednesday, September 21, 2011

Exposing Multiple Binding Types For The Same Service Class In WCF

Have you ever wanted to expose multiple binding types in WCF for the same service class? Well I have but it is not directly apparent on how to accomplish this. My thought was, “I have a single service and I want it to be consumable by both net.tcp and http bindings. Not a big deal, right?” Well in the end the code needed to make this happen is not all that complex, but getting to the solution took some work as usual.

My thought process initially was to try and figure out how to make the WCF configuration allow this condition with a single service, but this method has some side effects. My 1st attempt was to create a single service with multiple endpoints and mex configurations, with each endpoint configuration having a different binding type (http and net.tcp). This actually works, but comes along with a side effect which I did not care for. The problem was when a client would consume my service (either via net.tcp or https) both endpoint configurations were added to the client. This isn't a huge deal, but I wanted the service configurations to deploy independently to prevent any confusion. Also if the client requests only the http binding endpoint, then that is all I want them to get; not the net.tcp configuration as well or vice versa.

Next I wanted to try using (2) different service configurations within the same single WCF service. However, a WCF service class can at most be exposed once in configuration by a single service configuration. If you try and configure (2) separate services differing only in endpoint binding configuration but attempt to consume the same service class for both services, you are going to get an error. "A child element named 'service' with same key already exists at the same configuration scope. Collection elements must be unique within the same configuration scope". Simply changing the 'name' property on the service configuration is not an option, because the 'name' property represents the class that implements the service contract. Arbitrarily changing the name will break the service.

The solution I came up with that solves all of these requirements is to have the single main service class that contains the implemented logic, Implement (2) additional new Interfaces that will allow distinction or uniqueness for the endpoint contract configuration. We will also add (2) new service contract classes that inherit the main service class and provide uniqueness for service class configuration. This masquerade allows making the services appear to be unique in consumption, but really point back to the same logic which was what was the original requirement.

As I mentioned before I do not want to expose multiple endpoints from a single service implementing a single contract due to the unwanted side effects, but rather have multiple services each with a single endpoint, implementing the same contract. To do this each service needs to be unique, but when attempting to make separate services each serve up the same service class implementing the contract, there is no uniqueness. The 'ServiceEndpointElement.Name Property' in configuration must point to a class within the service. Because we want (1) unique endpoint per service, we need a unique service class as well. The new classes Inherit from the primary Service class providing all the main service functionality, but yet provides a unique service class entry point for the ServiceEndpointElement.Name Property. Again, the reason we do not want a single service exposing multiple bindings, is because upon client consumption all (1...n) binding configurations for a single service are downloaded and configured even if the client only wanted say the 'net.tcp' binding. To reduce confusion, each service configured ultimately exposes the identical functionality but provides a separate service class value for the ServiceEndpointElement.Name Property.

Let's take a look at how to implement this solution. To begin, here are the (3) main service contracts:
<ServiceContract()>
Public Interface IMyWcfServiceTcp
Inherits IMyWcfService

End Interface

<ServiceContract()>
Public Interface IMyWcfServiceHttp
Inherits IMyWcfService

End Interface

<ServiceContract()>
Public Interface IMyWcfService

<OperationContract(Name:="MyMethod1")>
Sub MyMethod1()

<OperationContract(Name:="MyMethod2")>
Sub MyMethod2()

<OperationContract(Name:="MyMethod3")>
Sub MyMethod3()

End Interface
Next are the (3) classes which WCF configuration will use in the service configuration:
Public Class MyWcfServiceTcp
Inherits MyWcfService

End Class

Public Class MyWcfServiceHttp
Inherits MyWcfService

End Class

Public Class MyWcfService
Implements IMyWcfServiceTcp, IMyWcfServiceHttp

Public Sub MyMethod1() Implements IMyWcfService.MyMethod1
End Sub

Public Sub MyMethod2() Implements IMyWcfService.MyMethod2
End Sub

Public Sub MyMethod3() Implements IMyWcfService.MyMethod3
End Sub

End Class
And finally, here is the WCF service configuration:
<!--*****WCF Hosted Service Endpoints*****-->
<services>
<service behaviorConfiguration="MyWcfServiceTcpBehavior" name="MyWcfServiceTcp">
<endpoint address="" binding="netTcpBinding" bindingConfiguration="MyWcfServiceTcpEndpoint"
name="MyWcfServiceTcpEndpoint" bindingName="MyWcfServiceTcpEndpoint"
contract="IMyWcfServiceTcp" />
<endpoint address="mex" binding="mexTcpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="net.tcp://localhost:8000/MyServices/MyWcfService" />
</baseAddresses>
</host>
</service>
<service behaviorConfiguration="MyWcfServiceHttpBehavior" name="MyWcfServiceHttp">
<endpoint address="" binding="wsHttpBinding" bindingConfiguration="MyWcfServiceHttpEndpoint"
name="MyWcfServiceHttpEndpoint" bindingName="MyWcfServiceHttpEndpoint"
contract="IMyWcfServiceHttp" />
<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
<host>
<baseAddresses>
<add baseAddress="http://localhost:8001/MyServices/MyWcfService" />
</baseAddresses>
</host>
</service>
</services>
Notice how each service configuration points to its own class, and each endpoint points to its own individual contract. However we know under the covers, both services expose the identical functionality. The difference: choice of binding type, and ability to only get the single binding's configuration added to the client (not both types) upon consumption.

Take note - if having your client getting multiple binding configurations for requesting a single endpoint does not bother you then procedure is not needed. Just define a single service with multiple endpoint configurations and multiple mex endpoints and you are done. However, if exposing your service with multiple binding types and having the client only receive the single endpoints configuration is important, then this should help fulfill the requirement.

No comments:

Post a Comment