Self Host Duplex WCF Service with Discovery

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.