Chapter 28. Windows Communication Foundation (WCF)

28.1. Introduction

Spring's WCF support allows you to configure your WCF services via dependency injection and add additional behavior to them using Aspect-Oriented programming (AOP).

There are two approaches in the 1.2 M1 release for configuring your services with DI which are discussed in the following sections. One approach creates an implementation of your service interface (a dynamic proxy) that retrieves a configured instance of your service type from the Spring container. This dynamic proxy is then the final service type that is hosted. The second approach uses the extension points in WCF itself to delegate to the Spring container to create and configure your service type. Both approaches are discussed below.

As it is desirable to have just one approach, we would greatly appreciate your input regarding these two approaches as well as any other suggestions regarding WCF support.

For those who would like to get their feet wet right way, check out the WcfQuickStart application in the examples directory.

28.2. Configuring WCF services via Dependency Injection

In this approach the container will creates an implementation of your service interface (a dynamic proxy) that retrieves a configured instance of your service type from the Spring container. This dynamic proxy is then the final service type that is hosted.

28.2.1. Dependency Injection using dynamic proxies

In this approach you develop your WCF services as you would normally do. For example here is a sample service type taken from the quickstart example.

    [ServiceContract(Namespace = "http://Spring.WcfQuickStart")]
    public interface ICalculator
    {
        [OperationContract]
        double Add(double n1, double n2);
        [OperationContract]
        double Subtract(double n1, double n2);
        [OperationContract]
        double Multiply(double n1, double n2);
        [OperationContract]
        double Divide(double n1, double n2);
        [OperationContract]
        string GetName();
    }

The implementation for the methods is fairly obvious but an additional property, SleepInSeconds, is present. This is the property we will configure via dependency injection. Here is a partial listing of the implementation

    public class CalculatorService : ICalculator
    {
        private int sleepInSeconds;

        public int SleepInSeconds
        {
            get { return sleepInSeconds; }
            set { sleepInSeconds = value; }
        }

        public double Add(double n1, double n2)
        {
            Thread.Sleep(sleepInSeconds*1000);
            return n1 + n2;

        }


        //  additional implementation not shown for brevity

    }

To configure this object with Spring, provide the XML configuration metadata as shown below as you would with any Spring managed object.

      <object id="calculator" singleton="false" type="Spring.WcfQuickStart.CalculatorService, Spring.WcfQuickStart.ServerApp">
        <property name="SleepInSeconds" value="1"/>
      </object>
[Note]Note

The object must be declared as a 'prototype' object, i.e. not a singleton, in order to interact correctly with WCF instancing.

To host this service type in a standalone application define an instance of a Spring.ServiceModel.Activation.ServiceHostFactoryObject and set is property TargetName to the id value of the previously defined service type. ServiceHostFactoryObject is a Spring IFactoryObject implementation. (See here for more information on IFactoryObjects and their interaction with the container.) The ServiceHostFactoryObject will create an instance of Spring.ServiceModel.Activation.SpringServiceHost that will be the ServiceHost instance associated with your service type. This configuration for this step is shown below.

      <object id="calculatorServiceHost" type="Spring.ServiceModel.Activation.ServiceHostFactoryObject, Spring.Services">
        <property name="TargetName" value="calculator" />
      </object>

Additional service configuration can be done declaratively in the standard App.config file as shown below

<system.serviceModel>
  <services>
    <service name="calculator" behaviorConfiguration="DefaultBehavior">
      <host> ... </host>
      <endpoint> ... </endpoint>
    </service>
    ...
  </services>

</system.serviceModel>
[Note]Note

It is important that the name of the service in the WCF declarative configuration section match the name of the Spring object definition

Spring.ServiceModel.Activation.SpringServiceHost is where the dynamic proxy for your service type is generated. This dynamic proxy will implement a single 'WCF' interface, the same on that your service type implements. The implementation of the service interface methods on the proxy will delegate to a wrapped 'target' object which is the object instance retrieved by name from the Spring container using the Spring API, ApplicationContext.GetObject(name). Since the object retrieved in this manner is fully configured, your WCF service is as well.

Outside of a standalone application you can also use the class Spring.ServiceModel.Activation.ServiceHostFactory (which inherits from System.ServiceModel.Activation.ServiceHostFactory) to host your services so that they can be configured via dependency injection. To use the dynamic proxy approached described here you should still refer to the name of the service as the name of the object definition used to configure the service type in the Spring container.

There are not many disadvantages to this approach other than the need to specify the service name as the name of the object definition in the Spring container and to ensure that singleton=false is used in the object definition. You can also use Spring.ServiceModel.Activation.ServiceHostFactory to host your service inside IIS but should still refer to the service by the name of the object in the Spring container.

28.2.2. Dependency Injection using WCF extensibility points.

The second approach uses the extensibility points in WCF itself to delegate to Spring to create and configure your WCF service. This approach was first taken (afaik) by Oran Dennison on his blog and several other folks on the web since then. In this approach Spring specific implementations of the WCF interfaces System.ServiceModel.Dispatcher.IInstanceProvider and System.ServiceModel.Description.IServiceBehavior are used to integrate Spring directly into the instancing of WCF services.

Spring's implementation of IInstanceProvider is Spring.ServiceModel.Support.SpringInstanceProvider. This implementation will look for an object by type in the Spring container and retrieve an instance configured using DI. If there is more than one object of the type registered with the container than an exception will be thrown. The SpringInstanceProvider is used by a custom service behavior class, Spring.ServiceModel.Support.SpringServiceBehavior where it is applied to all the service endpoints. This behavior is then added to the custom service host Spring.ServiceModel.Activation.SpringServiceHost

The service type is used to locate the object in the container. In your .svc file you specify the custom service host type and also the type of the service. Here is an example taken from the WcfQuickStart application that shows the use of this approach inside IIS.

<%@ ServiceHost Language="C#" Debug="true" Service="Spring.WcfQuickStart.CalculatorService" 
                                           Factory="Spring.ServiceModel.Activation.ServiceHostFactory" %>

The Spring configuration for the object is shown below.

      <object id="calculator" type="Spring.WcfQuickStart.CalculatorService, App_Code" singleton="false">
        <property name="SleepInSeconds" value="1"/>
      </object>
[Note]Note

The object must be declared as a 'prototype' object, i.e. not a singleton, in order to interact correctly with WCF instancing.

While integrating 'natively' with WCF does seem to be the most natural approach there is one 'gotya' that needs to be investigated further to see if there is an acceptable workaround in order for this approach to be viable. The issue is that if the service is configured to be a singleton, for example using [ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)] then the invocation of the IInstanceProvider is short-circuited. See the notes on the MSDN class documentation here. One workaround, which is not very appealing, is to use the PerCall instancing mode but set the singleton attribute in the Spring configuration to true, this way the same instance is always returned.

28.3. Apply AOP advice to WCF services

In either approach to performing dependency injection you can apply additional AOP advice to your WCF services in the same way as you have always done in Spring. The following configuration shows how to apply some simple performance monitoring advice to all services in the Spring.WcfQuickStart namespace and is taken from the QuickStart example.

      <object id="serviceOperation" type="Spring.Aop.Support.SdkRegularExpressionMethodPointcut, Spring.Aop">
        <property name="pattern" value="Spring.WcfQuickStart.*"/>
      </object>
      
      <object id="perfAdvice" type="Spring.WcfQuickStart.SimplePerformanceInterceptor, Spring.WcfQuickStart.ServerApp">
        <property name="Prefix" value="Service Layer Performance"/>
      </object>
      
      <aop:config>
        <aop:advisor pointcut-ref="serviceOperation" advice-ref="perfAdvice"/>
      </aop:config>

The aop:config section implicitly uses Spring's autoproxying features to add additional behavior to any objects defined in the container that match the pointcut criteria.

28.4. Creating client side proxies declaratively

To create a client side proxy based on the use of ChannelFactory<T>, you can use this rather ugly 'boiler plate' XML snippit that takes uses Spring's support for calling factory methods on object instances.

  <!-- returns ChannelFactory<ICalculator>("calculatorEndpoint").CreateChannel() -->

  <object id="serverAppCalculator" type="Spring.WcfQuickStart.ICalculator, Spring.WcfQuickStart.ClientApp"
          factory-object="serverAppCalculatorChannelFactory"
          factory-method="CreateChannel" />

  <object id="serverAppCalculatorChannelFactory"
          type="System.ServiceModel.ChannelFactory&lt;Spring.WcfQuickStart.ICalculator>, System.ServiceModel">

    <constructor-arg name="endpointConfigurationName" value="serverAppCalculatorEndpoint" />

  </object>
[Note]Note

This will be shortened using a custom namespce in the 1.2 RC1 release

The value 'serverAppCalculatorEndpoint' refers to the name of an enpoints in the <client> section of the standard WCF configuration inside of App.config.