DiscoveryClient and DynamicEndpoint

DiscoveryClient and DynamicEndpoint are two classes used on the client side to search for services. DiscoveryClient provides you with a list of services that match a specific set of criteria and allows you to connect to the services. DynamicEndpoint performs the same operation and in addition, automatically connects to one of the services that was found. Any endpoint can be made into a DynamicEndpoint, the search criteria can also be added in configuration, thus DynamicEndpoint is useful when you need discovery in your solution but do not want to modify the client logic – you only need to modify the endpoints. DiscoveryClient on the other hand can be used to gain finer control over your search operation. The uses and benefits of each are elaborated below.

DiscoveryClient

The DiscoveryClient defines synchronous and asynchronous Find methods, FindCompleted and FindProgressChanged events. It also defines synchronous and asynchronous Resolve methods and a ResolveCompleted event. Use the Find or FindAsync methods to search for services. Both of these methods take a FindCriteria instance that allows you to specify contract type names, scopes, maximum number of results requested, and scope matching rules. The FindCompleted and FindProgressChanged events can be used when calling the FindAsync method. FindProgressChanged is fired whenever the DiscoveryClient receives a response from a service. It can be used to display a progress bar showing the progress of the find operation. It can also be used to act on find responses as they are received. The FindCompleted event is fired when the find operation completes. This may happen because the maximum number of responses has been received or if the Duration has elapsed. When the find operation completes the results are returned in a FindResponse instance. The FindResponse contains a collection of EndpointDiscoveryMetadata which contains the addresses, contract type names, extensions, listen URIs, and scopes of the matching services. You can then use this information to connect to and call one of the matching services. The following example shows how to call the System.ServiceModel.Discovery.DiscoveryClient.Find(System.ServiceModel.Discovery.FindCriteria) method and use the returned metadata to call the found service. A benefit of using Find(FindCriteria) is that you can cache the list of endpoints you’ve found and use them at a later time. With this cache, you can build custom logic to handle various failure conditions.

DiscoveryClient dc = new DiscoveryClient(new UdpDiscoveryEndpoint());  
  
FindCriteria criteria = new FindCriteria(typeof(ICalculatorService));  
FindResponse fr = dc.Find(criteria);  
  
if (fr.Endpoints.Count > 0)  
{  
   EndpointAddress ep = fr.Endpoints[0].Address;  
   CalculatorServiceClient client = new CalculatorServiceClient();  
  
    // 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);  
}  
else  
   Console.WriteLine("No matching endpoints found");  

The following example shows how to perform a find operation asynchronously.

static void FindServiceAsync()  
{  
   DiscoveryClient dc = new DiscoveryClient(new UdpDiscoveryEndpoint());
   dc.FindCompleted += new EventHandler<FindCompletedEventArgs>( discoveryClient_FindCompleted);  
   dc.FindProgressChanged += new EventHandler<FindProgressChangedEventArgs>(discoveryClient_FindProgressChanged);  
   dc.FindAsync(new FindCriteria(typeof(ICalculatorService)));
}
static void discoveryClient_FindProgressChanged(object sender, FindProgressChangedEventArgs e)  
{  
   Console.WriteLine("Found service at: " + e.EndpointDiscoveryMetadata.Address  
}
  
static void discoveryClient_FindCompleted(object sender, FindCompletedEventArgs e)  
{
      if (e.Result.Endpoints.Count > 0)  
            {  
                EndpointAddress ep = e.Result.Endpoints[0].Address;  
                CalculatorServiceClient client = new CalculatorServiceClient();  
  
                // Connect to the discovered service endpoint  
                client.Endpoint.Address = ep;  
                Console.WriteLine("Invoking CalculatorService at {0}", ep);  
  
                double value1 = 100.00D;  
                double value2 = 15.99D;  
  
                double result = client.Add(value1, value2);  
                Console.WriteLine("Add({0},{1}) = {2}", value1, value2, result);  
            }  
            else  
                Console.WriteLine("No matching endpoints found");  
  
        }  

Use the Resolve and ResolveAsync(ResolveCriteria) methods to locate a service based on its endpoint address. This is useful when the endpoint address is not network addressable. The Resolve methods take an instance of ResolveCriteria which allows you to specify the endpoint address of the service you are resolving, the maximum duration of the resolve operation, and a set of extensions. The following example shows how to use the Resolve method to resolve a service.

DiscoveryClient dc = new DiscoveryClient(new UdpDiscoveryEndpoint());  
ResolveCriteria criteria = new ResolveCriteria(endpointAddress);  
ResolveResponse response = dc.Resolve(criteria);  
EndpointAddress newEp = response.EndpointDiscoveryMetadata.Address;  

DynamicEndpoint

DynamicEndpoint is a standard endpoint (For more information, see Standard Endpoints) which performs discovery and automatically selects a matching service. Just create a DynamicEndpoint passing in the contract to search for and the binding to use and pass the DynamicEndpoint instance to the WCF client. The following example shows how to create and use a DynamicEndpoint to call the calculator service. The discovery is performed every time the client is opened. Any endpoint defined in configuration can also be turned into a DynamicEndpoint by adding the kind ="dynamicEndpoint" attribute to the endpoint configuration element.

DynamicEndpoint dynamicEndpoint = new DynamicEndpoint(ContractDescription.GetContract(typeof(ICalculatorService)), new WSHttpBinding());  
CalculatorServiceClient client = new CalculatorServiceClient(dynamicEndpoint);  
  
Console.WriteLine("Invoking CalculatorService");  
Console.WriteLine();  
  
double value1 = 100.00D;  
double value2 = 15.99D;  
  
double result = client.Add(value1, value2);  
Console.WriteLine("Add({0},{1}) = {2}", value1, value2, result);  

See also