Tuesday, February 14, 2012

Calling a WCF service from a client without having the contract interface

I was asked yesterday in the Hebrew C#/.NET Framework MSDN forums a tough question – is it possible to dynamically call a WCF service using only the contract name, operation name, and metadata address?

At first, I agreed with the answer given in the forum – move from SOAP bindings to WebHttpBinding (“REST”). This of course makes things a lot easier, only requiring you to create a WebHttpRequest and parse the response. However the question remains - is it possible to do this in the case of a SOAP-based service endpoint?

The short answer is – YES!

The full answer is – YES, but you’ll need to do a lot of coding to make it work properly, and even more coding for complex scenarios (who said passing a data contract?)

How is it done you ask?

First let’s start with the contract – you have a simple contract that looks like so:


[ServiceContract]
public interface ICalculator
{
   [OperationContract]
   double Add(double n1, double n2);
  
   [OperationContract]
   double Subtract(double n1, double n2);

   [OperationContract]
   double Multiply(double n1, double n2);

   [OperationContract]
   double Divide(double n1, double n2);
}


At this point, the implementation doesn’t matter, but you can assume the service compiles and loads successfully.

Second, make sure your service has either a MEX endpoint or metadata exposed over HTTP GET. Read here for more info about the difference between the two.

Third – do the client coding!!! To create the client code, I took some ideas from the following links:

    http://msdn.microsoft.com/en-us/library/ms733780.aspx – Generating client-side type information for WCF contracts
    http://www.codeproject.com/Articles/42278/Call-a-Web-Service-Without-Adding-a-Web-Reference – The same concept of dynamic calls, but for ASP.NET web services (ASMX)


 using System;
 using System.Collections.Generic;
 using System.Linq;
 using System.ServiceModel;
 using System.ServiceModel.Description;
 using System.Globalization;
 using System.Collections.ObjectModel;
 using System.CodeDom.Compiler;
 
 namespace Client
 {
     class Program
     {
         static void Main(string[] args)
         {
             // Define the metadata address, contract name, operation name,
             // and parameters.
             // You can choose between MEX endpoint and HTTP GET by
             // changing the address and enum value.
             Uri mexAddress = new Uri("http://localhost:8732/CalculatorService/?wsdl");
             // For MEX endpoints use a MEX address and a
             // mexMode of .MetadataExchange
             MetadataExchangeClientMode mexMode = MetadataExchangeClientMode.HttpGet;
             string contractName = "ICalculator";
             string operationName = "Add";
             object[] operationParameters = new object[] { 1, 2 };
 
             // Get the metadata file from the service.
             MetadataExchangeClient mexClient = new MetadataExchangeClient(mexAddress, mexMode);
             mexClient.ResolveMetadataReferences = true;
             MetadataSet metaSet = mexClient.GetMetadata();
 
             // Import all contracts and endpoints
             WsdlImporter importer = new WsdlImporter(metaSet);
             Collection<ContractDescription> contracts =  importer.ImportAllContracts();
             ServiceEndpointCollection allEndpoints = importer.ImportAllEndpoints();
 
             // Generate type information for each contract
             ServiceContractGenerator generator = new ServiceContractGenerator();
             var endpointsForContracts = new Dictionary<string, IEnumerable<ServiceEndpoint>>();
 
             foreach (ContractDescription contract in contracts)
             {
                 generator.GenerateServiceContractType(contract);
                 // Keep a list of each contract's endpoints
                 endpointsForContracts[contract.Name] = allEndpoints.Where(
                     se => se.Contract.Name == contract.Name).ToList();
             }
 

Read more: Codeproject
QR: Calling-a-WCF-service-from-a-client-without-having

Posted via email from Jasper-Net