Lately, I’ve been writing and speaking a lot about WCF 4.5, but while delivering my “What’s new in WCF 4” session in Visual Studio Live yesterday, I realized that there is one feature of WCF 4 that most people are not aware of, and do not really understand how useful it is – Standard Endpoints.
In WCF, we always have to specify a set of address+binding+contract (ABC) for our endpoints. If our endpoints also need to be configured, for example – change the binding configuration, or the endpoint behavior, then we need to add some more configuration. We can use default configuration (another feature of WCF 4), but if we have two common settings, we cannot set two defaults and we’re back to square one.
Standard endpoints change the way we define endpoints – with standard endpoints, we specify a special “kind” name in our endpoint, which automatically sets our endpoint’s address, binding, contract, binding configuration, and endpoint behavior.
For example – if we define the following endpoint:
<endpoint address="mex" kind="mexEndpoint"/>
The above endpoint will automatically be set with the mexHttpBinding
and the IMetadataExchange
contract.
If we define the following endpoints:
<endpoint code="web" kind="webHttpEndpoint" contract="MyNS.IMyContract"/>
We will get an endpoint which uses webHttpBinding
, and automatically gets the webHttp
endpoint behavior.
Although this is quite nice, this is the least we can do with standard endpoints. The real use of standard endpoints is when you create some of your own.
Imagine the following – you are part of an infrastructure team in your organization and you need to explain to the dev teams which endpoint configuration they should use in their projects – “Please use NetTcp binding, with increased message size limits, with either security none or transport, and don’t forget to increase the send timeout”.
One way to do this is to send a memo to all the dev teams, hoping everyone follows your instructions to the letter. Another way you can do that is to create your own standard endpoint with all of the above configuration and just send it to the dev teams to use.
First of all, you need to create your custom endpoint:
1: public class CompanyNameStandardEndpoint : ServiceEndpoint
2: {
3: private bool _isSecured;
4:
5: public CompanyNameStandardEndpoint(ContractDescription contract)
6: : base(contract)
7: {
8: this.Binding = new NetTcpBinding();
9: ResetBindingConfiguration(this.Binding);
10: this.IsSystemEndpoint = false;
11: }
12:
13: public bool IsSecured
14: {
15: get
16: {
17: return _isSecured;
18: }
19: set
20: {
21: _isSecured = value;
22: if (_isSecured)
23: {
24: (this.Binding as NetTcpBinding).Security.Mode = SecurityMode.Transport;
25: }
26: else
27: {
28: (this.Binding as NetTcpBinding).Security.Mode = SecurityMode.None;
29: }
30: }
31:
32: }
33:
34:
35:
36: private void ResetBindingConfiguration(dynamic binding)
37: {
38: binding.SendTimeout = TimeSpan.FromMinutes(5);
39: binding.MaxReceivedMessageSize = Int32.MaxValue;
40: binding.MaxBufferSize = Int32.MaxValue;
41: }
42: }
Line 8 makes sure that your endpoint will use NetTcp binding.
Line 9 will call a method that initializes the binding settings (lines 36-41).
Note: The ResetBindingConfiguration
method receives a dynamic object because for some reason, some of the binding properties such as the MaxReceivedMessageSize
and the MaxBufferSize
are defined in each of the bindings instead of being defined in a base Binding
class. The dynamic will allow us to change our code later on to support both TCP and HTTP bindings without duplicating our method for overloads.
Line 10 specifies that this is a user-defined endpoint and not a system endpoint.
Lines 13-32 are responsible for handling the user’s selection to whether the endpoint is secured or not by changing the security mode to either Transport
or None
.
So now, we have a new standard endpoint that initializes the binding to NetTcpBinding
, sets the timeout and message size, and knows to set the security according to the user’s selection. We can now add this endpoint in code to our service by calling the following code:
1: CompanyNameStandardEndpoint newEndpoint = new CompanyNameStandardEndpoint(
2: ContractDescription.GetContract(typeof(IService1)));
3:
4: newEndpoint.IsSecured = false;
5: newEndpoint.Address = new EndpointAddress(tcpBaseAddress + "companyUnsecured");
6:
7: host.AddServiceEndpoint(newEndpoint);
To be able to add this endpoint configuration in the config file, you will need to add some boilerplate code:
1: public class CompanyNameStandardEndpointCollectionElement :
2: StandardEndpointCollectionElement<CompanyNameStandardEndpoint,
CompanyNameStandardEndpointElement>
3: {
4: }
5:
6: public class CompanyNameStandardEndpointElement : StandardEndpointElement
7: {
8: protected override ServiceEndpoint CreateServiceEndpoint
(ContractDescription contractDescription)
9: {
10: return new CompanyNameStandardEndpoint(contractDescription);
11: }
12:
13: public bool IsSecured
14: {
15: get { return (bool)base["isSecured"]; }
16: set { base["isSecured"] = value; }
17: }
18:
19: protected override ConfigurationPropertyCollection Properties
20: {
21: get
22: {
23: ConfigurationPropertyCollection properties = base.Properties;
24: properties.Add(new ConfigurationProperty("isSecured",
typeof(bool), false, ConfigurationPropertyOptions.None));
25: return properties;
26: }
27: }
28:
29: protected override Type EndpointType
30: {
31: get { return typeof(CompanyNameStandardEndpoint); }
32: }
33:
34: protected override void OnApplyConfiguration(ServiceEndpoint endpoint,
ServiceEndpointElement serviceEndpointElement)
35: {
36: CompanyNameStandardEndpoint customEndpoint = (CompanyNameStandardEndpoint)endpoint;
37: customEndpoint.IsSecured = this.IsSecured;
38: }
39:
40: protected override void OnApplyConfiguration(ServiceEndpoint endpoint,
ChannelEndpointElement channelEndpointElement)
41: {
42: CompanyNameStandardEndpoint customEndpoint = (CompanyNameStandardEndpoint)endpoint;
43: customEndpoint.IsSecured = this.IsSecured;
44: }
45:
46: protected override void OnInitializeAndValidate(
ServiceEndpointElement serviceEndpointElement)
47: {
48:
49: }
50:
51: protected override void OnInitializeAndValidate(
ChannelEndpointElement channelEndpointElement)
52: {
53:
54: }
55: }
The above code is a basic configuration element code. The most important part is lines 13-17 in which you need to repeat each of the properties you created in the custom standard element (for a mapping between XML and CLR) and line 24 where you add all the properties that can be set in the configuration file, so the configuration can be validated.
Once you create the above code, you need just one more step to use the new endpoint kind in your configuration – you need to tell WCF that you have a new service endpoint. To do that, you add the following XML in your <system.serviceModel>
section:
1: <extensions>
2: <endpointExtensions>
3: <add
4: name="companyNameEndpoint"
5: type="TestWcfStandardEndpoints.CompanyNameStandardEndpointCollectionElement,
TestWcfStandardEndpoints"/>
6: </endpointExtensions>
7: </extensions>
Note: On MSDN, you can find a good explanation on standard endpoints, but the extensions configuration part is incorrect, the above configuration is the correct one (the correct element in the <extensions>
is <endpointExtensions>
and not <standardEndpointExtensions>
as it appears in the article).
Now, you are ready to declare your new endpoints and configure them:
1: <services>
2: <service name="TestWcfStandardEndpoints.Service1">
3: <endpoint binding="basicHttpBinding"
4: contract="TestWcfStandardEndpoints.IService1"/>
5: <endpoint address="mex"
6: kind="mexEndpoint" />
7: <endpoint address="companySecured"
8: kind="companyNameEndpoint"
9: endpointConfiguration="securedEndpoint"
10: contract="TestWcfStandardEndpoints.IService1"/>
11: </service>
12: </services>
13:
14: <standardEndpoints>
15: <companyNameEndpoint>
16: <standardEndpoint isSecured="true" name="securedEndpoint"/>
17: </companyNameEndpoint>
18: </standardEndpoints>
In lines 7-10, we define the endpoint with the new “kind” (line 9) and specify where we configure the rest of the endpoint (line 10).
Lines 14-18 contains the configuration of the standard endpoint which we created.
So to conclude, standard endpoints are an easy way to create fully-configured endpoints with binding configuration, contract settings, and endpoint behaviors. It’s mostly useful when wanting to create the same endpoint over and over again in multiple projects (which happens in 99.99% of the time).
Don’t bother copy pasting all the above code – you can just download the complete solution from here.