“http://robbincremers.me/2012/01/01/wcf-custom-binding-by-configuration-and-by-binding-standardbindingelement-and-standardbindingcollectionelement/”
WCF Custom Binding by configuration and by Binding,
StandardBindingElement and StandardBindingCollectionElement
In the attempt to master the basic
topics and extensibility points of WCF, we are going to look to WCF Custom Bindings and their possibilities.
For certain scenario’s, our company
would like to use a netHttpBinding, which is not a default binding. Thus our
netHttpBinding will be a custom binding which uses BinaryEncoding and
HttpTransport, in contrast to the default http bindings which use
TextMessageEncoding and HttpTransport. Binary encoding is a proprietary .NET
protocol, but in our cases this is not an issue as we don’t have to be
interopable. We know the clients are .NET clients, somewhere around the globe
and to gain performance at the encoding, we want to use a custom httpbinding that uses binary encoding.
This might not be a ideal scenario,
but it is good enough to run past the basic principles.
1.
Custom binding by configuration:
Our solutions look as following:
We have 2 projects in our solution:
“WCF.Binding.Custom.client”: Client console application, used to mimic a client
“WCF.Binding.Custom.Service”: WCF Service Application, with 1 WCF service called “SaasService”
“WCF.Binding.Custom.client”: Client console application, used to mimic a client
“WCF.Binding.Custom.Service”: WCF Service Application, with 1 WCF service called “SaasService”
Our service contract:
Our service implementation:
Our web.config of our WCF service
application:
Note we defined a customBinding at
our <bindings> section and called it “netHttpBinding”. The binding
uses binaryMessageEncoding and httpTransport. Our endpoint has customBinding
set as binding, while the bindingConfiguration points to “netHttpBinding”,
which is our name of the custom binding configuration.
You can also configure other things
on the custom binding, like transactionFlow, security and so forth. At the
lowest level the Transport should be defined and the next one to define is the
encoding that will be used. Both these settings are required for any custom
binding. The other settings like reliableSession, transactionFlow, security, …
are possible, but not required and depend on what binding you want to create.
We host the WCF service by our local
IIS (Properties – Tab Web – Create Virtual Directory):
Our local url to our WCF service is : http://localhost/WCF.Binding.Custom.Service/SaasService.svc
When adding a service reference to our client to our localhost url of our IIS hosted service, our app.config looks like this:
It reads the custom binding from the
exposed WSDL and appends the default values to the configuration.
Our client console application code:
And when executing the client
console application:
2.
What about a reusable Custom Binding that can be used over multiple projects,
like the default bindings ?
We created a custom binding that
fits some of our custom scenario’s and we configured this custom binding by
configuration. But what if I have to use this custom binding over another 10
projects ? Do I have to copy this custom binding configuration to each ? What
if for some reason the custom binding has to add a bindingElement, do you have
to change the other 9 projects aswell then ?
There is also a possibility to
create a custom binding that behaves the same way as the default bindings. A
binding that you can select, just as you can chose basicHttpBinding and the
custom binding is configurable by configuration, just as the other default
bindings are. We create a class library that holds this custom binding and we
share the class library between the projects. Each project can still configure
some binding settings, like maxMessageSize etc in the configuration, but the
build of the custom binding is identical for each project.
We want to create our custom binding
NetHttpBinding again, but make it reusable and configurable by configuration,
just like any other default binding.
We start by creating a class library
for our NetHttpBinding:
Make sure you already add the
System.ServiceModel reference to the newly created class library.
To create a reusable and configurable custom binding, there are a few steps you have to go through:
To create a reusable and configurable custom binding, there are a few steps you have to go through:
1.
Create your custom binding with the abstract class Binding
We will start by creating a new
class called “NetHttpBinding”, which will be our custom binding.
Our solution looks like this:
Our solution looks like this:
Our NetHttpBinding will inherit from
the abstract class System.ServiceModel.Channels.Binding:
Implement the abstract class from
the Binding class and this is what we get:
So we must override 2 methods of our
Binding class:
The createBindingElements
has to return a bindingElementCollection that
holds all the bindingElements for the custom binding. This is the
same as what we did in our web.config configuration for our custom binding, but
now it is set by code. The second method we have to override is the “Scheme“ method, which returns the
used scheme for the binding. In our case, this is a very simple binding, that
only allows http transport, so we return “http” hardcoded. In a real scenario,
you will most likely support multiple protocols like http + https, so in that
case you would have to save your transport element in your NetHttpBinding class
and get the scheme from the transport element that has been chosen by the
configuration set on the binding.
This binding is now usable by code,
so you can create a netHttpBinding and configure it by code. (Though you
have to make NetHttpBinding class public then, as it is currently defined
internal)
To make the binding configurable by configuration in configuration file, there are two more steps to be taken:
To make the binding configurable by configuration in configuration file, there are two more steps to be taken:
2.
Create a Custom BindingElement with the Configuration.StandardBindingElement
Next step in line is to create a
BindingElement for our NetHttpBinding, by inheriting from System.ServiceModel.Configuration.StandardBindingElement.
As you notice we are working with System.ServiceModel.Configuration, which is
to make our NetHttpBinding usable and configurable by configuration file, just
like the other default bindings.
You will need to add a reference to System.Configuration.
You will need to add a reference to System.Configuration.
We create a class called “NetHttpBindingElement”,
which inherits from StandardBindingElement:
There are 2 methods that have to be
overridden, the BindingElementType and the OnApplyConfiguration method.
BindingElementType: Returns the type of the binding, which in our case is the
NetHttpBinding (:Binding) type
OnApplyConfiguration: Set the binding settings you want to use by default for the NetHttpBinding
OnApplyConfiguration: Set the binding settings you want to use by default for the NetHttpBinding
3.
Create a custom BindingCollectionElement with the
Configuration.StandardBindingCollectionElement
To finalize our custom
NetHttpBinding to be configurable by configuration,
we need to create a class that sets the StandardBindingCollectionElement.
The class we create that inherits from the StandardBindingCollectionElement is
also the class we will need to point to when we register the extension in our
configuration.
We will create a class called “NetHttpBindingCollectionElement”:
(click to enlarge)
We have to pass the binding and the
bindingconfiguration to the StandardBindingCollectionElement<x,y>. Our
binding is the “NetHttpBinding” and our binding configuration is the “NetHttpBindingElement”
The class itself is empty, only the
definition of the StandardBindingCollectionElement with what binding and what
binding configuration to use is what is important.
This is the class we will reference to for the registration of the binding extension.
This is the class we will reference to for the registration of the binding extension.
Our solution looks like this now:
4.
Use the custom binding NetHttpBinding at our service
We will start by adding a reference
to our NetHttpBinding class library at our WCF Service Application.
Next we adapt the web.config to register our custom NetHttpBinding and our endpoint to use this NetHttpBinding:
Next we adapt the web.config to register our custom NetHttpBinding and our endpoint to use this NetHttpBinding:
The same as with behaviors, we
register extensions at the
<extensions> node. Since we created a binding, we use <bindingExtensions> and register the custom
binding we created. We call it netHttpBinding and the type is the Full
Namespace+name of the StandardBindingCollectionElement, in our case “NetHttpBinding.NetHpttBindingCollectionElement”
and the second parameter is the name of the library.
We registered our custom binding as “netHttpBinding”
at our extensions, so we can set the binding of our endpoint to
netHttpBinding.
To test our new custom binding, update the service reference at the client. The app.config looks as following:
To test our new custom binding, update the service reference at the client. The app.config looks as following:
To our client this a custom binding,
which it also is.
When running the client console application:
When running the client console application:
5.
What about adding configuration elements to our custom binding
One of the uses of
StandardBindingElement is for configuration.
If we want to add custom properties to our custom binding and be able to set
these properties on our custom binding by configuration, we need to add some
extra code to our NetHttpBindingElement, which inherits from the
StandardBindingElement.
Our NetHttpBindingElement looks as
following:
We created a ConfigurationProperty
called “useBinaryEncoding” of which the default value is true.
We also overridden the Properties property of the StandardBindingElement, and we add “useBinaryEncoding” to the possible ConfigurationProperties of our binding for configuration. Note this is for configuration.
We added some code to the OnApplyConfiguration(Binding binding), which gets the netHttpBinding and sets the custom property on the NetHttpBinding to the value of our configuration property of useBinaryEncoding.
Finally we override the InitializeFrom method which initializes the from the binding.
We also overridden the Properties property of the StandardBindingElement, and we add “useBinaryEncoding” to the possible ConfigurationProperties of our binding for configuration. Note this is for configuration.
We added some code to the OnApplyConfiguration(Binding binding), which gets the netHttpBinding and sets the custom property on the NetHttpBinding to the value of our configuration property of useBinaryEncoding.
Finally we override the InitializeFrom method which initializes the from the binding.
Our NetHttpBinding, which inherits
from System.ServiceModel.Channels.Binding, looks like this:
We added a property, which holds the
same name as the property we defined for our Configuration property, named “UseBinaryEncoding”.
At our CreateBindingElements() method we changed so that we check the UseBinaryEncoding property value, before we decided what message encoding we will use for our custom binding.
If the UseBinaryEncoding is set to true (the default value) we will use the BinaryMessageEncodingBindingElement. If we chose to set the “useBinaryEncoding” to false in the configuration, a MtomMessageEncodingBindingElement will be used.
At our CreateBindingElements() method we changed so that we check the UseBinaryEncoding property value, before we decided what message encoding we will use for our custom binding.
If the UseBinaryEncoding is set to true (the default value) we will use the BinaryMessageEncodingBindingElement. If we chose to set the “useBinaryEncoding” to false in the configuration, a MtomMessageEncodingBindingElement will be used.
Make sure to rebuild the class
library NetHttpBinding if you want to use the new changes for the service.
Let’s adapt our service
configuration to use our new configuration setting:
As you can see, we added a binding
configuration for the “netHttpBinding” which sets the useBinaryEncoding
to true. At our endpoint we reference to the bindingConfiguration we created
for the NetHttpBinding.
If you now build the service and visit the local url, you should get the default WCF service screen. If you misconfigured any configuration property, you’ll get an error.
If you now build the service and visit the local url, you should get the default WCF service screen. If you misconfigured any configuration property, you’ll get an error.
At our client console application,
update the service reference. The app.config should look like this:
This looks identical to our custom
service without the useBinaryEncoding setting, which it also should.
If we do set the value of “useBinaryEncoding” to false in our service configuration, the Mtom message encoder should be used:
If we do set the value of “useBinaryEncoding” to false in our service configuration, the Mtom message encoder should be used:
If we update our service reference
again at the client console application, our app.config looks as following:
You’ll notice our custom binding has
changed to a wsHttpBinding with messageEncoding “Mtom”. The reason for this is
that HttpTransport + MtomMessageEncoding falls within the possibilities of the
wsHttpBinding, so the client generates a wsHttpBinding, which will have the
same channels as our custom channel, so they will be able to communicate. In
theory it was kind of a bad example of mine, but it still proves the point of
configuration settings on the custom binding. Our HttpTransport +
BinaryEncoding does not fall within the range of any possible default binding,
so a custom binding gets generated then.
Our default bindings are just also a
set of bindingElements. The bindingElementCollections that were most used, the
grouped as the default bindings and gave them a name.
If we run our client console
application with the wsHttpBinding configuration and our service which has the
netHttpBinding set:
Even though the the service side has
a NetHttpBinding and the client side registered for a WsHttpBinding, the
service and client are still able of communicating. At runtime, both the
bindings create exactly the same binding channels (an HttpTransport and a
MtomMessageEncoding binding element), thus they will be able to communicate
since they channel will be identical for both.
Any suggestions, remarks or
improvements are always welcome.
If you found this information useful, make sure to support me by leaving a comment.
If you found this information useful, make sure to support me by leaving a comment.
Cheers and