How to: Programmatically Add Discoverability to a WCF Service and Client

This topic explains how to make a Windows Communication Foundation (WCF) service discoverable. It is based on the Self-Host sample.

To configure the existing Self-Host service sample for Discovery

  1. Open the Self-Host solution in Visual Studio 2012. The sample is located in the TechnologySamples\Basic\Service\Hosting\SelfHost directory.

  2. Add a reference to System.ServiceModel.Discovery.dll to the service project. You may see an error message saying "System. ServiceModel.Discovery.dll or one of its dependencies requires a later version of the .NET Framework than the one specified in the project …" If you see this message, right-click the project in the Solution Explorer and choose Properties. In the Project Properties window, make sure that the Target Framework is .NET Framework 4.6.1.

  3. Open the Service.cs file and add the following using statement.

    using System.ServiceModel.Discovery;
    
  4. In the Main() method, inside the using statement, add a ServiceDiscoveryBehavior instance to the service host.

    public static void Main()
    {
        // Create a ServiceHost for the CalculatorService type.
        using (ServiceHost serviceHost = new ServiceHost(typeof(CalculatorService)))
        {
            // Add a ServiceDiscoveryBehavior
            serviceHost.Description.Behaviors.Add(new ServiceDiscoveryBehavior());
    
            // ...
        }
    }
    

    The ServiceDiscoveryBehavior specifies that the service it is applied to is discoverable.

  5. Add a UdpDiscoveryEndpoint to the service host right after the code that adds the ServiceDiscoveryBehavior.

    // Add ServiceDiscoveryBehavior
    serviceHost.Description.Behaviors.Add(new ServiceDiscoveryBehavior());
    
    // Add a UdpDiscoveryEndpoint
    serviceHost.AddServiceEndpoint(new UdpDiscoveryEndpoint());
    

    This code specifies that discovery messages should be sent to the standard UDP discovery endpoint.

To create a client application that uses discovery to call the service

  1. Add a new console application to the solution called DiscoveryClientApp.

  2. Add a reference to System.ServiceModel.dll and System.ServiceModel.Discovery.dll

  3. Copy the GeneratedClient.cs and App.config files from the existing client project to the new DiscoveryClientApp project. To do this, right-click the files in the Solution Explorer, select Copy, and then select the DiscoveryClientApp project, right-click and select Paste.

  4. Open Program.cs.

  5. Add the following using statements.

    using System.ServiceModel;
    using System.ServiceModel.Discovery;
    using Microsoft.ServiceModel.Samples;
    
  6. Add a static method called FindCalculatorServiceAddress() to the Program class.

    static EndpointAddress FindCalculatorServiceAddress()
    {
    }
    

    This method uses discovery to search for the CalculatorService service.

  7. Inside the FindCalculatorServiceAddress method, create a new DiscoveryClient instance, passing in a UdpDiscoveryEndpoint to the constructor.

    static EndpointAddress FindCalculatorServiceAddress()
    {
        // Create DiscoveryClient
        DiscoveryClient discoveryClient = new DiscoveryClient(new UdpDiscoveryEndpoint());
    }
    

    This tells WCF that the DiscoveryClient class should use the standard UDP discovery endpoint to send and receive discovery messages.

  8. On the next line, call the Find method and specify a FindCriteria instance that contains the service contract you want to search for. In this case, specify ICalculator.

    // Find ICalculatorService endpoints
    FindResponse findResponse = discoveryClient.Find(new FindCriteria(typeof(ICalculator)));
    
  9. After the call to Find, check to see if there is at least one matching service and return the EndpointAddress of the first matching service. Otherwise return null.

    if (findResponse.Endpoints.Count > 0)
    {
        return findResponse.Endpoints[0].Address;
    }
    else
    {
        return null;
    }
    
  10. Add a static method named InvokeCalculatorService to the Program class.

    static void InvokeCalculatorService(EndpointAddress endpointAddress)
    {
    }
    

    This method uses the endpoint address returned from FindCalculatorServiceAddress to call the calculator service.

  11. Inside the InvokeCalculatorService method, create an instance of the CalculatorServiceClient class. This class is defined by the Self-Host sample. It was generated using Svcutil.exe.

    // Create a client
    CalculatorClient client = new CalculatorClient();
    
  12. On the next line, set the endpoint address of the client to the endpoint address returned from FindCalculatorServiceAddress().

    // Connect to the discovered service endpoint
    client.Endpoint.Address = endpointAddress;
    
  13. Immediately after the code for the previous step, call the methods exposed by the calculator service.

    Console.WriteLine("Invoking CalculatorService at {0}", endpointAddress);
    
    double value1 = 100.00D;
    double value2 = 15.99D;
    
    // Call the Add service operation.
    double result = client.Add(value1, value2);
    Console.WriteLine("Add({0},{1}) = {2}", value1, value2, result);
    
    // Call the Subtract service operation.
    result = client.Subtract(value1, value2);
    Console.WriteLine("Subtract({0},{1}) = {2}", value1, value2, result);
    
    // Call the Multiply service operation.
    result = client.Multiply(value1, value2);
    Console.WriteLine("Multiply({0},{1}) = {2}", value1, value2, result);
    
    // Call the Divide service operation.
    result = client.Divide(value1, value2);
    Console.WriteLine("Divide({0},{1}) = {2}", value1, value2, result);
    Console.WriteLine();
    
    //Closing the client gracefully closes the connection and cleans up resources
    client.Close();
    
  14. Add code to the Main() method in the Program class to call FindCalculatorServiceAddress.

    public static void Main()
    {
        EndpointAddress endpointAddress = FindCalculatorServiceAddress();
    }
    
  15. On the next line, call the InvokeCalculatorService() and pass in the endpoint address returned from FindCalculatorServiceAddress().

    if (endpointAddress != null)
    {
        InvokeCalculatorService(endpointAddress);
    }
    
    Console.WriteLine("Press <ENTER> to exit.");
    Console.ReadLine();
    

To test the application

  1. Open an elevated command prompt and run Service.exe.

  2. Open a command prompt and run Discoveryclientapp.exe.

  3. The output from service.exe should look like the following output.

    Received Add(100,15.99)
    Return: 115.99
    Received Subtract(100,15.99)
    Return: 84.01
    Received Multiply(100,15.99)
    Return: 1599
    Received Divide(100,15.99)
    Return: 6.25390869293308
    
  4. The output from Discoveryclientapp.exe should look like the following output.

    Invoking CalculatorService at http://localhost:8000/ServiceModelSamples/service
    Add(100,15.99) = 115.99
    Subtract(100,15.99) = 84.01
    Multiply(100,15.99) = 1599
    Divide(100,15.99) = 6.25390869293308
    
    Press <ENTER> to exit.
    

Example

The following is a listing of the code for this sample. Because this code is based on the Self-Host sample, only those files that are changed are listed.

// Service.cs
using System;
using System.Configuration;
using System.ServiceModel;
using System.ServiceModel.Discovery;

namespace Microsoft.ServiceModel.Samples
{
    // See SelfHost sample for service contract and implementation
    // ...

        // Host the service within this EXE console application.
        public static void Main()
        {
            // Create a ServiceHost for the CalculatorService type.
            using (ServiceHost serviceHost = new ServiceHost(typeof(CalculatorService)))
            {
                // Add the ServiceDiscoveryBehavior to make the service discoverable
                serviceHost.Description.Behaviors.Add(new ServiceDiscoveryBehavior());
                serviceHost.AddServiceEndpoint(new UdpDiscoveryEndpoint());

                // Open the ServiceHost to create listeners and start listening for messages.
                serviceHost.Open();

                // The service can now be accessed.
                Console.WriteLine("The service is ready.");
                Console.WriteLine("Press <ENTER> to terminate service.");
                Console.WriteLine();
                Console.ReadLine();
            }
        }
    }
}
// Program.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.ServiceModel.Discovery;
using Microsoft.ServiceModel.Samples;
using System.Text;

namespace DiscoveryClientApp
{
    class Program
    {
        static EndpointAddress FindCalculatorServiceAddress()
        {
            // Create DiscoveryClient
            DiscoveryClient discoveryClient = new DiscoveryClient(new UdpDiscoveryEndpoint());

            // Find ICalculatorService endpoints
            FindResponse findResponse = discoveryClient.Find(new FindCriteria(typeof(ICalculator)));

            if (findResponse.Endpoints.Count > 0)
            {
                return findResponse.Endpoints[0].Address;
            }
            else
            {
                return null;
            }
        }

        static void InvokeCalculatorService(EndpointAddress endpointAddress)
        {
            // Create a client
            CalculatorClient client = new CalculatorClient();

            // Connect to the discovered service endpoint
            client.Endpoint.Address = endpointAddress;

            Console.WriteLine("Invoking CalculatorService at {0}", endpointAddress);

            double value1 = 100.00D;
            double value2 = 15.99D;

            // Call the Add service operation.
            double result = client.Add(value1, value2);
            Console.WriteLine("Add({0},{1}) = {2}", value1, value2, result);

            // Call the Subtract service operation.
            result = client.Subtract(value1, value2);
            Console.WriteLine("Subtract({0},{1}) = {2}", value1, value2, result);

            // Call the Multiply service operation.
            result = client.Multiply(value1, value2);
            Console.WriteLine("Multiply({0},{1}) = {2}", value1, value2, result);

            // Call the Divide service operation.
            result = client.Divide(value1, value2);
            Console.WriteLine("Divide({0},{1}) = {2}", value1, value2, result);
            Console.WriteLine();

            //Closing the client gracefully closes the connection and cleans up resources
            client.Close();
        }
        static void Main(string[] args)
        {
            EndpointAddress endpointAddress = FindCalculatorServiceAddress();

            if (endpointAddress != null)
            {
                InvokeCalculatorService(endpointAddress);
            }

            Console.WriteLine("Press <ENTER> to exit.");
            Console.ReadLine();

        }
    }
}

See also