次の方法で共有


WCF - Let's Start The Dialogue

With a current project as background I had another look at the duplex channel communication options in Windows Communication Foundation (WCF). Duplex channels are a great means to break the usual request/response pattern in web services communication. And they are useful as there are quite some scenarios where you would like to establish a bi-directional communication channel between server and client and not to be trapped in the drawbacks of strong typed, tightly coupled, network quality dependent remoting technologies. The great thing about Windows Communication Foundation is that it supports several transport mechanisms and protocols and provides standard bindings for those and for almost any transport option there are bindings that support duplex communication. If you are not familiar with the WCF terminologies I recommend you read this introduction on MSDN. In particular the bindings supporting duplex conversation styles are:

  • WSDualHttpBinding
  • NetTcpBinding
  • NetNamedPipeBinding
  • NetPeerTcpBinding

In my little scenario I just wanted to make sure that a client registered once can receive notifications by the server. So indeed the registering process is actually only triggering a set of multiple callbacks. Due to that the service contract here only includes one operation contract. However since the interesting and important part is the callback I also need to define a service contract for the callback whose methods have obviously to be implemented on the client side.

 

Server Portion

So what I do here for the server side is the following:

  • define a service contract with one operation contract exposing a interface for clients to register themselves
  • define a service contract for the callback method
  • implement the operation contract for the server service contract

Especially important is that I make the service aware about the callback contract and its respective interface in the ServiceContract attribute of the service. Another thing to mention is that I define the operations as one way operations. This ensures that the callback can be received by the client instantly after sending the outgoing message because the message is already processed and no response is expected from the server. If the client would expect a response for this specific message, it would lead to a deadlock situation. In that case we would need to set the ConcurrencyMode to reentrant or multiple in order to allow out-of-order processing of messages.

Below you can find the code for my small sample:

using System;
using System.ServiceModel;
using System.Runtime.Serialization;

// A WCF service consists of a contract (defined below as IMyService, DataContract1),
// a class which implements that interface (see MyService),
// and configuration entries that specify behaviors associated with
// that implementation (see <system.serviceModel> in web.config)

namespace WCFTests
{
[ServiceContract(CallbackContract=typeof(IPingServiceClientContract))]
public interface IPingService
{
[OperationContract(IsOneWay=true)]
void RegisterClient();
}

    public interface IPingServiceClientContract
{
[OperationContract(IsOneWay = true)]
void Ping(string clientID);
}

    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
public class PingService : IPingService
{
private Guid clientID;
IPingServiceClientContract callback = null;

        public PingService()
{
clientID = Guid.NewGuid();
callback = OperationContext.Current.GetCallbackChannel<IPingServiceClientContract>();

}
public void RegisterClient()
{
int counter = 1;
callback.Ping(clientID.ToString() + " : " + counter);
for (int i = 0; i < 20; i++)
{
System.Threading.Thread.Sleep(2000);
counter++;
callback.Ping(clientID.ToString() + " : " + counter);

}

}
}
}

As I am going to host the service in Internet Information Server (IIS) which gives me additional quality of services and I do not have to care about the hosting environment I need to create a .svc file and a web.config. The .svc file really only carries some metadata that gives the IIS the necessary information to find the code artefacts of the service. To create that is quite straight forward however you have to notice that when you are using Visual Studio 2005 that it generates the Service class without defining a namespace. So if you add the class to a specific namespace you have to specify the full qualified name of your class in the Service attribute of the .svc file like shown below.

<% @ServiceHost Language=C# Debug="true" Service="WCFTests.PingService" CodeBehind="~/App_Code/Service.cs" %>

The Web.config also contains information about the service, especially endpoint and service behaviour information. One pitfall I fell into is that the name attribute of the service element doesn't take an arbitrary name defined by the user but must again specify the fully qualified class name of the service class. I found that to be a tricky one since this is not the first thought one would have when dealing with a name attribute and it is also not very consistent across the other elements of a service configuration. In earlier releases there was a serviceType attribute which in my opinion was the better option because it clearly states what data it expects and therefore is less confusing.

Another thing to pay attention to is that you need to specify that the service exposes the service metadata via http requests if you want to create a service proxy from the wsdl using the svcutil.exe tool. If you don't do that you will get a html page stating this requirement as response of a request to the wsdl using a browser. For my sample I use WSDualHttpBinding in order to allow duplex converstaion style. The web.config of my sample is printed below:

<?xml version="1.0"?>
<configuration xmlns="
https://schemas.microsoft.com/.NetConfiguration/v2.0 ">
<system.serviceModel>
<services>
<!-- Before deployment, you should remove the returnFaults behavior configuration to avoid disclosing information in exception messages -->
<service name="WCFTests.PingService" behaviorConfiguration="returnFaults">
<endpoint contract="WCFTests.IPingService" binding="wsDualHttpBinding"/>
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="returnFaults" >
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceMetadata httpGetEnabled="true" />
</behavior>
</serviceBehaviors>
</behaviors>
</system.serviceModel>
<system.web>
<compilation debug="true"/>
</system.web>
</configuration>

When deploying the service to the IIS you have to pay attention that you need to create a new application in the IIS manager if you are using Vista. Creating a virtual directory is no more sufficient which is a difference to earlier releases of IIS as far as I remember. In some special cases it can also happen that your IIS doesn't recognize the .svc extension. This can happen if there is, for whatever reason, no handler registered for the service files. If you are facing this problem you can run the ServiceModelReg.exe tool with the i switch to reregister the handlers. The tool is usually located under <System Drive>:\Windows\Microsoft.NET\Framework\v3.0\Windows Communication Foundation.
When that's all done you can try your service by pointing your browser to the URL of the service. If all works you can start to create the client.

 

Client Portion 

The first thing you probably would do is to create a proxy class using the svcutil.exe tool. This will create the class file and also a configuration file which you can use straight away. Now for the client itself you need to implement the operation contract specified in the service contract of the service. I found it best to create a callback type implementing the callback interface defined in the proxy file created earlier.
When finished doing that you can actually create a proxy instance. In our case we do that by instancing a InstanceContext object and passing in an instance of our callback type as parameter for the instantiation. Then you can actually create a client proxy using the InstanceContext object just instantiated passed in as a parameter to the constructor of the proxy object. At this point it is worth to mention that the way to create the proxy significantly changed in the evolution of WCF from Beta 1 till now. Earlier you had to create a ServiceSite object that was passed to the constructor of the proxy. The ServiceSite type is no more available in WCF. After having created the proxy you can use it to register your client. After that the server is able to send notifications to the client and doesn't need to wait for another call from the client in order to update information on the client.

The client code for this little sample is printed below:

using System;
using System.Collections.Generic;
using System.Text;
using System.ServiceModel;

namespace WCFPingServiceClient
{
class PingServiceClientApp
{
static void Main(string[] args)
{
InstanceContext context = new InstanceContext(new PingServiceCallback());
PingServiceClient client = new PingServiceClient(context);
try
{
client.Open();
Console.WriteLine("Registering Client");
client.RegisterClient();
Console.ReadKey();
}
finally
{
client.Close();
}
}
}

    class PingServiceCallback : IPingServiceCallback
{
public void Ping(string clientID)
{
Console.WriteLine("Received Ping from Server! Client ID: " + clientID);
}
}
}

Regarding the configuration you can paste in the configuration section from the configuration that was generated by the svcutil.exe tool. I include one screenshot of how the sample looks like on the client and it nicely shows that because I specified the InstanceContextMode.PerSession as an service behaviour a new service instance is created for every client that registers with the service.

Comments

  • Anonymous
    May 23, 2008
    If you're building Web services or if you're implementing SOA on the Microsoft platform , then you're