To begin with my service testing I need to create a class library to hold the interfaces that will be implemented by the WCF service. The first is the interface that defines the functionality the service will implement, the second is the callback interface that is required for the duplex nature of the service being implemented.
using System.ServiceModel; namespace SharedInterfaces { [ServiceContract(CallbackContract = typeof(IWCFTrainingCallback))] public interface IWCFTraining { [OperationContract] string GetName(); [OperationContract(IsOneWay = true)] void GetStatus(); } public interface IWCFTrainingCallback { [OperationContract] void Status(string statusMessage); } }
The service will be hosted in a console application. Once the console application has been created there are two things that need to be performed.
First creating a class to hold the service implementation.
[ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Reentrant)] class WCFTrainingService : IWCFTraining { string IWCFTraining.GetName() { // For testing just return a string return "Jester98x"; } void IWCFTraining.GetStatus() { // We're only testing so no need to do much // more than just loop round for a while and // return a value back to the callback for (var i = 0; i < 100; i++) { System.Threading.Thread.Sleep(50); OperationContext.Current .GetCallbackChannel<IWCFTrainingCallback>() .Status(i.ToString() + "% complete"); } } }
Then we get round to creating the self-hosted part with the following:
class Program { static void Main(string[] args) { Console.Title = "WCF Testing Host"; Uri baseAddress = new Uri("http://localhost:9080"); Uri tcpBase = new Uri("net.tcp://localhost:9081"); using (var selfHost = new ServiceHost( typeof(WCFTrainingService), baseAddress, tcpBase)) { selfHost.AddServiceEndpoint(new UdpDiscoveryEndpoint()); var sdb = new ServiceDiscoveryBehavior(); selfHost.Description.Behaviors.Add(sdb); selfHost.AddServiceEndpoint( typeof(IWCFTraining), new NetHttpBinding(), "HttpTrainingService"); var net = new NetTcpBinding(); selfHost.AddServiceEndpoint( typeof(IWCFTraining), new NetTcpBinding(), "TcpTrainingService"); var smb = new ServiceMetadataBehavior(); smb.HttpGetEnabled = true; selfHost.Description.Behaviors.Add(smb); selfHost.Open(); Console.WriteLine("Service has started"); Console.ReadLine(); selfHost.Close(); } } }
At this point we have a service hosted within a console application. Doesn’t do a great deal, that’s fine as we’re just showing that discovery is working and that we can communicate in a full duplex fashion.
So the next thing that is required is a client to assist with the testing of the service. One thing that I want to ensure is that it is proxy free, this is why the interfaces are in a shared project, i.e. not confined to the service.
The client is another console application.
class Program { // Simple list to store the discovered endpoints static List<EndpointAddress> found = new List<EndpointAddress>(); static void Main(string[] args) { Console.Write("Wait for proxy and press enter:"); Console.ReadLine(); FindWCFService(); Console.ReadLine(); } private static void FindWCFService() { Console.WriteLine("Starting Discovery Process"); var dc = new DiscoveryClient(new UdpDiscoveryEndpoint()); var fc = new FindCriteria(typeof(IWCFTraining)); // Only search for 5 seconds fc.Duration = TimeSpan.FromSeconds(5); // Set up some event handlers dc.FindProgressChanged += Dc_FindProgressChanged; dc.FindCompleted += Dc_FindCompleted; // Start the discovery process dc.FindAsync(fc); dc.Close(); } private static void Dc_FindCompleted(object sender, FindCompletedEventArgs e) { if (found != null && found.Count > 0) { // Define the object that will handle the callback // from the service var ptest = new ProcessTest(); // Get an instance context from the object var ic = new InstanceContext(ptest); var net = new NetTcpBinding(); // Create a channel to call the service methods var duplexProxy = new DuplexChannelFactory<IWCFTraining>(ic, net, found[1]); var dProxy = duplexProxy.CreateChannel(); Console.WriteLine("Calling status on proxy"); // Call the get status method, which will act as our // long running process and call back test bed dProxy.GetStatus(); // Call a method that returns immediately Console.WriteLine(dProxy.GetName()); } } private static void Dc_FindProgressChanged(object sender, FindProgressChangedEventArgs e) { // When an endpoint is found add it to our found list found.Add(e.EndpointDiscoveryMetadata.Address); } } [CallbackBehavior(UseSynchronizationContext = false, IncludeExceptionDetailInFaults = true)] public class ProcessTest : IWCFTrainingCallback { public void Status(string statusMessage) { // Do something with the callback Console.WriteLine(statusMessage); } }
That’s about it for now. Thanks for reading and see you next time.