288 lines
13 KiB
XML
288 lines
13 KiB
XML
<?xml version="1.0" encoding="UTF-8"?>
|
|
<!--
|
|
/*
|
|
* Copyright 2002-2008 the original author or authors.
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
-->
|
|
<chapter version="5" xml:id="wcf" xmlns="http://docbook.org/ns/docbook"
|
|
xmlns:ns6="http://www.w3.org/1999/xlink"
|
|
xmlns:ns5="http://www.w3.org/1998/Math/MathML"
|
|
xmlns:ns4="http://www.w3.org/1999/xhtml"
|
|
xmlns:ns3="http://www.w3.org/2000/svg"
|
|
xmlns:ns="http://docbook.org/ns/docbook">
|
|
<title>Windows Communication Foundation (WCF)</title>
|
|
|
|
<section xml:id="wcf-introduction">
|
|
<title>Introduction</title>
|
|
|
|
<para>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).</para>
|
|
|
|
<para>For those who would like to get their feet wet right way, check out
|
|
the WcfQuickStart application in the examples directory.</para>
|
|
</section>
|
|
|
|
<section xml:id="wcf-di">
|
|
<title>Configuring WCF services via Dependency Injection</title>
|
|
|
|
<para>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>
|
|
<para>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 <link
|
|
ns6:href="http://orand.blogspot.com/2006/10/wcf-service-dependency-injection.html">blog</link>
|
|
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
|
|
<literal>[ServiceBehavior(InstanceContextMode=InstanceContextMode.Single)]
|
|
</literal>then the invocation of the IInstanceProvider is
|
|
short-circuited. See the notes on the MSDN class documentation <link
|
|
ns6:href="http://msdn.microsoft.com/en-us/library/system.servicemodel.dispatcher.iinstanceprovider.aspx">here</link>.
|
|
While this would be the preferred approach, no acceptable work around
|
|
was found.</para>
|
|
</note></para>
|
|
|
|
<section xml:id="wcf-di-proxy">
|
|
<title>Dependency Injection</title>
|
|
|
|
<para>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.</para>
|
|
|
|
<programlisting language="csharp"> [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();
|
|
}</programlisting>
|
|
|
|
<para>The implementation for the methods is fairly obvious but an
|
|
additional property, <literal>SleepInSeconds</literal>, is present. This
|
|
is the property we will configure via dependency injection. Here is a
|
|
partial listing of the implementation</para>
|
|
|
|
<programlisting language="csharp"> 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
|
|
|
|
}</programlisting>
|
|
|
|
<para>To configure this object with Spring, provide the XML
|
|
configuration metadata as shown below as you would with any Spring
|
|
managed object.</para>
|
|
|
|
<programlisting language="myxml"> <object id="calculator" singleton="false" type="Spring.WcfQuickStart.CalculatorService, Spring.WcfQuickStart.ServerApp">
|
|
<property name="SleepInSeconds" value="1"/>
|
|
</object>
|
|
</programlisting>
|
|
|
|
<note>
|
|
<para>The object must be declared as a 'prototype' object, i.e. not a
|
|
singleton, in order to interact correctly with WCF instancing.</para>
|
|
</note>
|
|
|
|
<para>To host this service type in a standalone application define an
|
|
instance of a
|
|
<literal>Spring.ServiceModel.Activation.ServiceHostFactoryObject</literal>
|
|
and set is property <literal>TargetName</literal> to the id value of the
|
|
previously defined service type.
|
|
<literal>ServiceHostFactoryObject</literal> is a Spring
|
|
<literal>IFactoryObject</literal> implementation. (See <link
|
|
linkend="objects-factory-lifecycle-factoryobject">here</link> for more
|
|
information on <literal>IFactoryObjects</literal> and their interaction
|
|
with the container.) The <literal>ServiceHostFactoryObject</literal>
|
|
will create an instance of
|
|
<literal>Spring.ServiceModel.Activation.SpringServiceHost</literal> that
|
|
will be the ServiceHost instance associated with your service type. This
|
|
configuration for this step is shown below.</para>
|
|
|
|
<programlisting language="myxml"> <object id="calculatorServiceHost" type="Spring.ServiceModel.Activation.ServiceHostFactoryObject, Spring.Services">
|
|
<property name="TargetName" value="calculator" />
|
|
</object></programlisting>
|
|
|
|
<para>Additional service configuration can be done declaratively in the
|
|
standard App.config file as shown below</para>
|
|
|
|
<programlisting language="myxml"><system.serviceModel>
|
|
<services>
|
|
<service name="calculator" behaviorConfiguration="DefaultBehavior">
|
|
<host> ... </host>
|
|
<endpoint> ... </endpoint>
|
|
</service>
|
|
...
|
|
</services>
|
|
|
|
</system.serviceModel></programlisting>
|
|
|
|
<note>
|
|
<para>It is important that the name of the service in the WCF
|
|
declarative configuration section match the name of the Spring object
|
|
definition</para>
|
|
</note>
|
|
|
|
<para><literal>Spring.ServiceModel.Activation.SpringServiceHost
|
|
</literal>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,
|
|
<literal>ApplicationContext.GetObject(name)</literal>. Since the object
|
|
retrieved in this manner is fully configured, your WCF service is as
|
|
well.</para>
|
|
|
|
<para>Outside of a standalone application you can also use the class
|
|
<literal>Spring.ServiceModel.Activation.ServiceHostFactory</literal>
|
|
(which inherits from
|
|
<literal>System.ServiceModel.Activation.ServiceHostFactory</literal>) 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.</para>
|
|
|
|
<para>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
|
|
<literal>Spring.ServiceModel.Activation.ServiceHostFactory</literal> to
|
|
host your service inside IIS but should still refer to the service by
|
|
the name of the object in the Spring container.</para>
|
|
</section>
|
|
</section>
|
|
|
|
<section xml:id="wcf-aop">
|
|
<title>Apply AOP advice to WCF services</title>
|
|
|
|
<para>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
|
|
<literal>Spring.WcfQuickStart</literal> namespace and is taken from the
|
|
QuickStart example.</para>
|
|
|
|
<programlisting language="myxml"> <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></programlisting>
|
|
|
|
<para>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.</para>
|
|
</section>
|
|
|
|
<section>
|
|
<title>Creating client side proxies declaratively</title>
|
|
|
|
<para>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 <link linkend="xsd-config-body-schemas-wcf">Spring WCF
|
|
Schema</link> for more information.</para>
|
|
|
|
<programlisting language="myxml"><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></programlisting>
|
|
|
|
<para>The value 'serverAppCalculatorEndpoint' refers to the name of an
|
|
enpoints in the <client> section of the standard WCF configuration
|
|
inside of App.config.</para>
|
|
</section>
|
|
|
|
<section>
|
|
<title>Exporting PONOs as WCF Services</title>
|
|
|
|
<para>Much like the approach taken for .asmx web services Spring provides
|
|
an exporter that will add <literal>[ServiceContract] </literal>and
|
|
<literal>[OperationContract]</literal> attributes by default to all public
|
|
interface methods on a given (PONO) class. The exporter class is
|
|
<literal>Spring.ServiceModel.ServiceExporter</literal> 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</para>
|
|
|
|
<programlisting language="myxml"><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></programlisting>
|
|
|
|
<para>Spring does not provide any means to add<literal>
|
|
[DataContract]</literal> or <literal>[DataMember]</literal> 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 <literal>ISerializable</literal> interface, having the
|
|
<literal>[Serializable]</literal> 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
|
|
<literal>DataContractSerializer</literal>.</para>
|
|
</section>
|
|
</chapter>
|