In this post, I'll demonstrate how to configure both the host and the client in code without the need for configuring services i the <system.serviceModel> section of the config-file. In fact, you don't need a <system.serviceModel> section at all. What you'll do need (and want) sometimes, is the Uri of the service in the configuration file. Configuring the Uri of the the service is actually only needed for the client or when self-hosting, not when hosting in IIS. So, exactly What do we need to configure? The binding type and the binding constraints The metadata behavior Debug behavior You can of course configure even more, and even more if you want to, WCF is after all the king of configuration… As an example I'll be hosting and consuming a service that removes most of the default constraints for WCF-services, using a BasicHttpBinding. Of course, in regards to security, it is probably better to have some constraints on the server, but this is only a demonstration. The ServerConfig class in the code beneath is a static helper class that will be used in the examples. In this post, I’ll be using this helper-class for all configuration, for both the server and the client. In WCF, the client and the server have both their own WCF-configuration. With this piece of code, they will be sharing the same configuration. 1: public static class ServiceConfig
2: {
3: public static Binding DefaultBinding
4: {
5: get
6: {
7: var binding = new BasicHttpBinding();
8: Configure(binding);
9: return binding;
10: }
11: }
12:
13: public static void Configure(HttpBindingBase binding)
14: {
15: if (binding == null)
16: {
17: throw new ArgumentException("Argument 'binding' cannot be null. Cannot configure binding.");
18: }
19:
20: binding.SendTimeout = new TimeSpan(0, 0, 30, 0); // 30 minute timeout
21: binding.MaxBufferSize = Int32.MaxValue;
22: binding.MaxBufferPoolSize = 2147483647;
23: binding.MaxReceivedMessageSize = Int32.MaxValue;
24: binding.ReaderQuotas.MaxArrayLength = Int32.MaxValue;
25: binding.ReaderQuotas.MaxBytesPerRead = Int32.MaxValue;
26: binding.ReaderQuotas.MaxDepth = Int32.MaxValue;
27: binding.ReaderQuotas.MaxNameTableCharCount = Int32.MaxValue;
28: binding.ReaderQuotas.MaxStringContentLength = Int32.MaxValue;
29: }
30:
31: public static ServiceMetadataBehavior ServiceMetadataBehavior
32: {
33: get
34: {
35: return new ServiceMetadataBehavior
36: {
37: HttpGetEnabled = true,
38: MetadataExporter = {PolicyVersion = PolicyVersion.Policy15}
39: };
40: }
41: }
42:
43: public static ServiceDebugBehavior ServiceDebugBehavior
44: {
45: get
46: {
47: var smb = new ServiceDebugBehavior();
48: Configure(smb);
49: return smb;
50: }
51: }
52:
53:
54: public static void Configure(ServiceDebugBehavior behavior)
55: {
56: if (behavior == null)
57: {
58: throw new ArgumentException("Argument 'behavior' cannot be null. Cannot configure debug behavior.");
59: }
60:
61: behavior.IncludeExceptionDetailInFaults = true;
62: }
63: }
Configuring the server
There are basically two ways to host a WCF service, in IIS and self-hosting. When hosting a WCF service in a production environment using SOA architecture, you'll be most likely hosting it in IIS. When testing the service in integration tests, it's very handy to be able to self-host services in the unit-tests. In fact, you can share the the WCF configuration for self-hosted services and services hosted in IIS. And that is exactly what you want to do, testing the same configurations for test and production environments.
Configuring when Self-hosting
When self-hosting, in order to start the service, you'll have to instantiate the ServiceHost class, configure the service and open it.
1: // Create the service-host.
2: var host = new ServiceHost(typeof(MyService), endpoint);
3:
4: // Configure the binding
5: host.AddServiceEndpoint(typeof(IMyService), ServiceConfig.DefaultBinding, endpoint);
6:
7: // Configure metadata behavior
8: host.Description.Behaviors.Add(ServiceConfig.ServiceMetadataBehavior);
9:
10: // Configure debgug behavior
11: ServiceConfig.Configure((ServiceDebugBehavior)host.Description.Behaviors[typeof(ServiceDebugBehavior)]);
12:
13: // Start listening to the service
14: host.Open();
15:
Configuring when hosting in IIS
When you create a WCF service application with the wizard in Visual Studio, you'll end up with bits and pieces of code in order to get the service running:
Svc-file with codebehind.
A interface to the service
Web.config
In order to get rid of the configuration in the <system.serviceModel> section, which the wizard has generated for us, we must tell the service that we have a factory that will create the service for us. We do this by changing the markup for the svc-file:
1: <%@ ServiceHost Language="C#" Debug="true" Service="Namespace.MyService" Factory="Namespace.ServiceHostFactory" %>
The markup tells IIS that we have a factory called ServiceHostFactory for this service.
The service factory has a method we can override which will be called when someone asks IIS for the service. There are overloads we can override:
1: System.ServiceModel.ServiceHostBase CreateServiceHost(string constructorString, Uri[] baseAddresses)
2: System.ServiceModel.ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
3:
In this example, we'll be using the last one, so our implementation looks like this:
1: public class ServiceHostFactory : System.ServiceModel.Activation.ServiceHostFactory
2: {
3:
4: protected override System.ServiceModel.ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
5: {
6: var host = base.CreateServiceHost(serviceType, baseAddresses);
7: host.Description.Behaviors.Add(ServiceConfig.ServiceMetadataBehavior);
8: ServiceConfig.Configure((ServiceDebugBehavior)host.Description.Behaviors[typeof(ServiceDebugBehavior)]);
9: return host;
10: }
11: }
12:
1: public class ServiceHostFactory : System.ServiceModel.Activation.ServiceHostFactory
2: {
3:
4: protected override System.ServiceModel.ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
5: {
6: var host = base.CreateServiceHost(serviceType, baseAddresses);
7: host.Description.Behaviors.Add(ServiceConfig.ServiceMetadataBehavior);
8: ServiceConfig.Configure((ServiceDebugBehavior)host.Description.Behaviors[typeof(ServiceDebugBehavior)]);
9: return host;
10: }
11: }
12:
As you can see, we are using the same configuration helper we used when self-hosting. Now, when you have a factory, the <system.serviceModel> section of the configuration can be removed, because the section will be ignored when the service has a custom factory. If you want to configure something else in the config-file, one could configure in some other section.
Configuring the client
Microsoft has helpfully created a ChannelFactory class in order to create a proxy client. When using this approach, you don't have generate those awfull proxy classes for the client. If you share the contracts with the server in it's own assembly like in the layer diagram under, you can share the same piece of code. The contracts in WCF are the interface to the service and if any, the datacontracts (custom types) the service depends on.
Using the ChannelFactory with our configuration helper-class is very simple:
1: var identity = EndpointIdentity.CreateDnsIdentity("localhost");
2: var endpointAddress = new EndpointAddress(endPoint, identity);
3: var factory = new ChannelFactory<IMyService>(DeployServiceConfig.DefaultBinding, endpointAddress);
4: using (var myService = new factory.CreateChannel())
5: {
6: myService.Hello();
7: }
8: factory.Close();
Happy configuration!