|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Announcements
Chapters
Services
Feature Zones
|
IntroductionMicrosoft has produced Windows Communication Foundation (WCF) as the natural successor to its earlier .NET Web Service and Remoting technologies. From the documentation it initially seems like WCF just uses the same sort of manual client proxy generation as Web Services. However, WCF is equally capable of generating client proxies automatically in a similar manner to Remoting. This article seeks to redress the documentation imbalance. BackgroundWeb Services were specifically designed for situations in which an interface was being publicly exposed outside the enterprise (where it was impractical to share interface code) and/or where technologies differ (for example, a Java client consuming a .NET Web Service). Clients are typically built by extracting interface metadata from a running Web Service, generating proxy interface code and building it into the client application. Remoting was designed for use within an enterprise, typically on a single machine or enterprise LAN/WAN. The client(s) and server(s) are effectively part of the same overall application or system, sharing interface assemblies from which client proxies are automatically generated at runtime. Often both client and server are built as part of the same solution or in the same build process, a build which can be completed in a single pass. Developers moving from Remoting must wonder how to generate WCF proxies automatically from shared assemblies as this subject is not well explained in the WCF documentation. There are strong reasons for keeping automatic proxies, including simpler build process and avoiding interface mismatch errors caused by missed manual proxy generation steps. Automatic proxies also give you common types between client and server which is essential if you want to share other code between the two. How does WCF provide us with automatic client proxies? Here is a simple example. The Service InterfaceFirstly we define our interface (contract) and decorate it with the normal WCF service and operation attributes. This is then compiled into the shared assembly, in this case ICalculator.dll. // ICalculator.cs
using System.ServiceModel;
namespace Calculator
{
[ServiceContract]
public interface ICalculator : IDisposable
{
[OperationContract]
int Add (int a, int b);
}
}
This topic is well covered in the WCF documentation, but it is worth pointing out the use of The Service ImplementationThe example server here implements the calculator service and also provides a very simple console hosting application. // CalculatorService.cs
using System;
using System.ServiceModel;
namespace Calculator
{
// Implementation of CalculatorService
public class CalculatorService : ICalculator
{
public CalculatorService ()
{
Console.WriteLine ("Constructing CalculatorService");
}
public int Add (int a, int b)
{
Console.WriteLine ("Call {0} Adding {1} and {2}", callNo++, a, b);
return a+b;
}
public void Dispose ()
{
Console.WriteLine ("Disposing CalculatorService");
}
private int callNo = 1;
}
// Simple console service host
public class TestProgram
{
public static void Main ()
{
ServiceHost svcHost = new ServiceHost (typeof (CalculatorService));
svcHost.Open();
Console.WriteLine ("Press a key to exit");
Console.ReadKey();
svcHost.Close();
}
}
}
The very simple console service hosting application above just starts up the <!--CalculatorService.config-->
<configuration>
<system.serviceModel>
<services>
<service name="Calculator.CalculatorService">
<endpoint address="net.tcp://localhost:5000/Calc"
binding="netTcpBinding"
contract="Calculator.ICalculator" />
<endpoint address="net.pipe://localhost/Calc"
binding="netNamedPipeBinding"
contract="Calculator.ICalculator" />
<endpoint address="http://localhost:8081/Calc"
binding="wsHttpBinding"
contract="Calculator.ICalculator" />
<endpoint address="http://localhost:8080/Calc"
binding="basicHttpBinding"
contract="Calculator.ICalculator" />
</service>
</services>
</system.serviceModel>
</configuration>
In this case I have exposed the same service using four endpoints, which correspond to the four bindings I will explore further below. The Client ProxySince this article is about automatic client proxies, this is where the real action takes place. Firstly of course, the client program must reference the shared interface assembly ICalculator.dll so it can use the Creating a proxy does not use ICalculator calc = new ChannelFactory<ICalculator>("TcpCalc").CreateChannel();
This statement creates both a proxy object of type Having created the proxy, the client can simply invoke the methods of the remote service object: int sum1 = calc.Add (1,2);
int sum2 = calc.Add (3,4);
There is a problem here. Even when we have finished with the client proxy, the channel to the server can remain open (for some protocols) and the remote service object will still exist, tying up valuable network and server resources. Only when the client proxy is garbage collected will the system clean-up. Fortunately, it just so happens that proxy objects constructed by calc.Dispose();
This seems just like a proxy call to a corresponding service Of course it is good practice to employ the using (ICalculator calc = new ChannelFactory<ICalculator>("TcpCalc").CreateChannel())
{
int sum1 = calc.Add (1,2);
int sum2 = calc.Add (3,4);
. . .
}
IMHO this last code snippet really illustrates the way that WCF clients should look when both client and service originate within the same organisation. Example Client// CalculatorClient.cs
using System;
using System.ServiceModel;
namespace Calculator
{
class TestClient
{
static void Main ()
{
// Create a proxy via TCP which is not closed until calc1 is garbage collected
ICalculator calc1 = new ChannelFactory<ICalculator>("TcpCalc").CreateChannel();
int sum1 = calc1.Add (1,2);
int sum2 = calc1.Add (3,4);
Console.WriteLine ("Sum1 = {0} Sum2 = {1}", sum1, sum2);
// Create a proxy via named pipe which explicitly closed by Dispose()
ICalculator calc2 = new ChannelFactory<ICalculator>("PipeCalc").CreateChannel();
sum1 = calc2.Add (5,6);
sum2 = calc2.Add (7,8);
calc2.Dispose();
Console.WriteLine ("Sum1 = {0} Sum2 = {1}", sum1, sum2);
// Create a proxy via WS HTTP binding which closes at the end of the 'using' block
using (ICalculator calc3 = new ChannelFactory<ICalculator>(
"WsHttpCalc").CreateChannel())
{
sum1 = calc3.Add (9,10);
sum2 = calc3.Add (11,12);
Console.WriteLine ("Sum1 = {0} Sum2 = {1}", sum1, sum2);
}
// Create a proxy via basic HTTP binding. Each call closes itself & 'using'
// does nothing
using (ICalculator calc4 = new ChannelFactory<ICalculator>(
"HttpCalc").CreateChannel())
{
sum1 = calc4.Add (13,14);
sum2 = calc4.Add (15,16);
Console.WriteLine ("Sum1 = {0} Sum2 = {1}", sum1, sum2);
}
}
}
}
A number of interesting points here:
The corresponding client config file defines the four named service endpoints: <!--CalculatorClient.config-->
<configuration>
<system.serviceModel>
<client>
<endpoint address="net.tcp://localhost:5000/Calc"
binding="netTcpBinding"
contract="Calculator.ICalculator"
name="TcpCalc" />
<endpoint address="net.pipe://localhost/Calc"
binding="netNamedPipeBinding"
contract="Calculator.ICalculator"
name="PipeCalc" />
<endpoint address="http://localhost:8081/Calc"
binding="wsHttpBinding"
contract="Calculator.ICalculator"
name="WsHttpCalc" />
<endpoint address="http://localhost:8080/Calc"
binding="basicHttpBinding"
contract="Calculator.ICalculator"
name="HttpCalc" />
</client>
</system.serviceModel>
</configuration>
ConclusionIn my experience, communications between clients and services within the enterprise is more common than exposing external interfaces to third parties. WCF provides an effective mechanism for this situation by automatically generating proxies using the The lack of documentation may also have deterred some Remoting users from moving to WCF. While WCF automatic proxies don’t solve all the issues of migration, they do knock down one of the barriers to adoption. In many cases WCF is the effective successor to Remoting, exhibiting comparable performance and a similar choice of protocols. Finally, some readers may be thinking that sharing types breaks the rules of SOA. This is not so. The 3rd tenet of SOA is "share schema/contract, not class". The shared interface types define the contract -- they are not classes (which would represent the implementation). I would argue that this approach is actually more service oriented than manually generated proxies. The first tenet of SOA is that boundaries should be explicit and the automatic creation of the client proxy via HistoryFirst version, 06/10/08.
|
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||