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:
[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: