Multiple Service Contracts and Indigo - Consuming internal services using shared contracts (Page 3 of 4 ) In this part of the lab, you will implement the internal client application and invoke service operations over TCP and named pipe protocols. The purpose of this exercise is to illustrate how you might share service metadata when you own both sides of the development effort for intranet clients. Sharing contract libraries ensures both sides are compiling against the latest contract versions throughout the development cycle. In addition, this exercise will illustrate the use of different standard bindings for TCP and named pipes. - First, go to theInternalClientproject and add a reference to theBusinessServiceContractsproject. This will give the client application direct access to the service contract necessary to invoke the service.
The client application requires prior knowledge of the service endpoints it can reach before it can configure a proxy. Under the assumption that you own both sides, client developers will manually configure client endpoints to reach each service endpoint. In this case, you'll consumeServiceAover TCP andServiceBover named pipes.
Open the app.config file and add the<system.serviceModel>section shown in Example 1-21. This includes two endpoints for each service: one for the main service contract, the other for the administrative contract. The binding for each endpoint matches the binding configuration for the same endpoint at the service. In this case, TCP endpoints usenetTcpBinding, and named pipe endpoints usenetNamedPipeBinding.
Example 1-21. Service model configuration for the InternalClient
<system.serviceModel> <client> <endpoint address="net.tcp://localhost:9000/ServiceA" contract="BusinessServiceContracts.IServiceA" binding="netTcpBinding" /> <endpoint address="net.tcp://localhost:9000/Admin" contract="BusinessServiceContracts.IAdmin" binding="netTcpBinding" name="TCP_IAdmin" /> <endpoint address="net.pipe://localhost/ServiceB" contract="BusinessServiceContracts.IServiceB" binding="netNamedPipeBinding" /> <endpoint address="net.pipe://localhost/Admin" contract="BusinessServiceContracts.IAdmin" binding="netNamedPipeBinding" name="IPC_IAdmin"/> </client> </system.serviceModel>
Remember that when you select a binding, you are selecting a transport protocol, a message encoding format, and possibly other messaging protocols for security and reliability, for example. The details of what goes into a binding will be discussed in Chapter 3. The important thing is that if you use the defaults on both sides, the communication channel at each end will be compatible.
- Now you will write some code to invoke each service endpoint on both services. To do this, you will need four proxy references: one for each service and contract.
Open Form1.cs and add ausingstatement forBusinessServiceContracts. In addition, declare a proxy for each contract, scoped to the lifetime of the application, adding the following definitions as members ofForm1:
using BusinessServiceContracts;
public partial class Form1 : Form { IServiceA m_proxyA; IAdmin m_adminProxyA;
IServiceB m_proxyB; IAdmin m_adminProxyB;
// more code }
In the form constructor, initialize each proxy usingChannelFactory<T>. The proxies forIServiceAandIServiceBcan use the default client endpoint for each contract. Because there is only one endpoint for each contract type, you don't have to specify which<endpoint>section will initialize the proxy.
SinceIAdminhas two endpoints defined, one for TCP and another for named pipes, you must specify which endpoint will initialize the proxy when you constructChannelFactory<T>. Do this by passing the appropriate endpoint name from Example1-21.
You'll see the resulting code for the form constructor in Example 1-22. You must also add a reference toSystem.ServiceModel, as shown.
Example 1-22. Code to initialize ServiceA and ServiceB proxies
using System.ServiceModel;
public Form1() { InitializeComponent();
ChannelFactory<IServiceA> factoryA = new ChannelFactory<IServiceA>(""); m_proxyA = factoryA.CreateChannel();
ChannelFactory<IAdmin> adminFactoryA = new ChannelFactory<IAdmin>("TCP_IAdmin"); m_adminProxyA = adminFactoryA.CreateChannel();
ChannelFactory<IServiceB> factoryB = new ChannelFactory<IServiceB>(""); m_proxyB = factoryB.CreateChannel();
ChannelFactory<IAdmin> adminFactoryB = new ChannelFactory<IAdmin>("IPC_IAdmin"); m_adminProxyB = adminFactoryB.CreateChannel(); }
The user interface for the client has already been created. If you look at Form1.cs in design view, you'll see a button to test each service and administrative operation. Now you'll add code to invoke each operation using the appropriate proxy. For each button on the form, add a handler for theClick event; you can do this by double-clicking each button from design view. Inside eachClick event handler, add code to invoke the appropriate operation and show the result in a message box. The resulting code for each of these handlers is shown in Example 1-23.
Just adding the code from Example 1-23 will not be sufficient to hook up the event handlers. When you double-click each button in design view, this generates designer code to hook up each event handler to its Button control.
Example 1-23. Code to invoke all service operations
private void button1_Click(object sender, EventArgs e) { string s = m_adminProxyA.AdminOperation1(); MessageBox.Show(s); } private void button2_Click(object sender, EventArgs e) { string s = m_adminProxyA.AdminOperation2(); MessageBox.Show(s); } private void button3_Click(object sender, EventArgs e) { string s = m_adminProxyB.AdminOperation1(); MessageBox.Show(s); } private void button4_Click(object sender, EventArgs e) { string s = m_adminProxyB.AdminOperation2(); MessageBox.Show(s); } private void button5_Click(object sender, EventArgs e) { string s = m_proxyA.Operation1(); MessageBox.Show(s); } private void button6_Click(object sender, EventArgs e) { string s = m_proxyA.Operation2(); MessageBox.Show(s); } private void button7_Click(object sender, EventArgs e) { string s = m_proxyB.Operation3(); MessageBox.Show(s); }
It is always good practice to release resources when you are finished with them. To make sure that each of the channels are properly disposed of when the application exits, add code to explicitly close each proxy. First, add a new private function namedCloseProxy()that will cast each proxy toICommunicationObjectand safely invoke itsClose()operation, swallowing any errors that may occur to prevent an exception during exit. Then, add an event handler for theFormClosingevent ofForm1 and add code to callCloseProxy()for each proxy. The code to add is shown in Example1-24.
Example 1-24. Code to close each proxy
private void CloseProxy(ICommunicationObject proxy) { try { if (proxy != null) proxy.Close(); } catch { } }
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{ CloseProxy(m_proxyA as ICommunicationObject); CloseProxy(m_proxyB as ICommunicationObject); CloseProxy(m_adminProxyA as ICommunicationObject); CloseProxy(m_adminProxyB as ICommunicationObject); }
- Compile the solution and test the internal client. Run theHostproject first, and then runInternalClient. Click each button to invoke operations exposed byServiceA andServiceB.
Next: Consuming external services with a generated proxy >>
More BrainDump Articles More By O'Reilly Media | This article is excerpted from chapter 1 of the book Learning WCF A Hands-on Guide, written by Michele Leroux Bustamante (O'Reilly, 2007; ISBN: 0596101627). Check it out today at your favorite bookstore. Buy this book now.
|
| |