Chapter 30. Windows Communication Foundation (WCF)

30.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).

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

30.2. Configuring WCF services via Dependency Injection

The technical approach used to perform dependency injection is based on dynamically creating 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.

[Note]Note

An alternative implementation approach that uses extensibility points in WCF to delegate to Spring to create and configure your WCF service was tried but proved to be limited in its range of applicability. This approach was first taken (afaik) by Oran Dennison on his blog and several other folks on the web since then. The issue in using this approach 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. While this would be the preferred approach, no acceptable work around was found.

30.2.1. Dependency Injection

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.

30.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.

30.4. Creating client side proxies declaratively

To create a client side proxy based on the use of ChannelFactory<T>, you can use Spring's WCF schema to create an instance of the interface that will communicate over a WCF channel. See section on the Spring WCF Schema for more information.

<objects xmlns="http://www.springframework.net"
         xmlns:wcf="http://www.springframework.net/wcf">

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

  <wcf:channelFactory id="serverAppCalculator"
       channelType="Spring.WcfQuickStart.ICalculator, Spring.WcfQuickStart.Contracts"
       endpointConfigurationName="serverAppCalculatorEndpoint" />
  
</objects>

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

You can also specify the scope of the created channel (default is singleton) and use classic DI to configure the ChannelFactory<T> instance.

<objects xmlns="http://www.springframework.net"
         xmlns:wcf="http://www.springframework.net/wcf">
         
  <wcf:channelFactory id="serverAppCalculator"
       channelType="Spring.WcfQuickStart.ICalculator, Spring.WcfQuickStart.Contracts"
       endpointConfigurationName="serverAppCalculatorEndpoint">
    <wcf:property name="IsSingleton" value="false" />
    <wcf:property name="Credentials.Windows.ClientCredential" value="Domain\Login:Password" />
  </wcf:channelFactory>
  
</objects>

30.5. Exporting POCOs as WCF Services

Much like the approach taken for .asmx web services Spring provides an exporter that will add [ServiceContract] and [OperationContract] attributes by default to all public interface methods on a given (POCO) class. The exporter class is Spring.ServiceModel.ServiceExporter and has various options to fine-tune what interfaces are exported and the specific attributes that get applied to each method and on that class. Here is a simple example

<object id="HelloWorldExporter" type="Spring.ServiceModel.ServiceExporter, Spring.Services">
    <property name="TargetName" value="HelloWorld"/>
    <property name="MemberAttributes">
        <dictionary>
            <entry key="SayHelloWorld">
                <object type="System.ServiceModel.OperationContractAttribute, System.ServiceModel">
                    <property name="IsOneWay" value="false"/>
                    <!-- configure any other OperationContractAttribute properties here -->
                </object>
            </entry>
        </dictionary>
    </property>
</object>

Spring does not provide any means to add [DataContract] or [DataMember] attributes to method arguments of your service operations. As such, either you will do that yourself or you may choose to use a serializer other than DataContractSerializer, for example one that relies on method arguments that implement the ISerializable interface, having the [Serializable] attribute, or are serializable via the XmlSerializer. Use the latter serializers is a good way to migrate from an existing RCP based approach, such as using .NET remoting, to WCF in order to take advantage of the WCF runtime and avoid editing much existing code. You can then incrementally refactor and/or create new operations that use DataContractSerializer.