Consuming WCF Service Via Reflection

This article provides a sample of consuming a WCF service on the go using reflection instead of adding references to our project or application.
 
In this sample, I will cover how to work with simple data types as well as complex data types while accessing the WCF Service via reflection.
 
To demonstrate using the sample, first of all create a simple WCF application. In it I have created two methods:
  1. GetTestString: It expects a parameter of string type and returns a string.
  2. GetTestDataUsingDataContract: It expects a complex data type and returns a complex data type.
Actually it is very simple, when you add a default WCF application, it will automatically give you two test methods with a default service contract and default bindings. So I haven't made many changes in it since the purpose of this article to let one consume a WCF Service via reflection.
 
IService1.cs
  1. [ServiceContract]  
  2. public interface IService1  
  3. {  
  4.     [OperationContract]  
  5.     string GetTestString(string value);  
  6.    
  7.     [OperationContract]  
  8.     CompositeType GetTestDataUsingDataContract(CompositeType composite);  
  9. }  
Default Composite Data Type you will automatically get is:
  1. [DataContract]  
  2. public class CompositeType  
  3. {  
  4.     bool boolValue = true;  
  5.     string stringValue = "Hello ";  
  6.     [DataMember]  
  7.     public bool BoolValue  
  8.     {  
  9.         get { return boolValue; }  
  10.         set { boolValue = value; }  
  11.     }  
  12.     [DataMember]  
  13.     public string StringValue  
  14.     {  
  15.         get { return stringValue; }  
  16.         set { stringValue = value; }  
  17.     }  
  18. }  
The Service class Service1.cs looks something like this:
  1. public class Service1 : IService1  
  2. {  
  3.     public string GetTestString(string value)  
  4.     {  
  5.         return string.Format("Welcome {0}", value);  
  6.     }  
  7.     public CompositeType GetTestDataUsingDataContract(CompositeType composite)  
  8.     {  
  9.         if (composite.BoolValue)  
  10.         {  
  11.             composite.StringValue += " From service";  
  12.         }  
  13.         return composite;  
  14.     }  
  15. }  
Now run this service, either by code or host it on IIS and get the service path.
 
Note: One can use various bindings while hosting the service as per their requirement, defining the bindings is out of the scope of this article.
 
Now create a Demo Website to check if the service can be loaded with reflection.
 
For this I have created a Default page in my Website:
 
Default.aspx
  1. <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>  
  2. <!DOCTYPE html>  
  3. <html xmlns="http://www.w3.org/1999/xhtml">  
  4. <head runat="server">  
  5.     <title></title>  
  6. </head>  
  7. <body>  
  8.     <form id="form1" runat="server">  
  9.         <div>  
  10.             <table>  
  11.                 <tr>  
  12.                     <td>  
  13.                         <asp:Label runat="server" Text="User"></asp:Label>  
  14.                     </td>  
  15.                     <td>  
  16.                         <asp:TextBox ID="txtUser" runat="server"></asp:TextBox>  
  17.                     </td>  
  18.                 </tr>  
  19.                 <tr>  
  20.                     <td>  
  21.                         <asp:Button ID="btnGetUserFromService" runat="server" Text="Get User Message" OnClick="btnGetUserFromService_Click"></asp:Button>  
  22.                     </td>  
  23.                     <td>  
  24.                         <asp:Label ID="lblUserMessage" runat="server"></asp:Label>  
  25.                     </td>  
  26.                 </tr>  
  27.             </table>  
  28.         </div>  
  29.         <div>  
  30.             <table>  
  31.                 <tr>  
  32.                     <td>  
  33.                         <asp:Label ID="lblCompostiteDataTypeStringValue" runat="server" Text="Enter String Value which will go to service in a composite data type"></asp:Label>  
  34.                     </td>  
  35.                     <td>  
  36.                         <asp:TextBox ID="txtCompostiteDataTypeStringValue" runat="server"></asp:TextBox>  
  37.                     </td>  
  38.                 </tr>  
  39.                 <tr>  
  40.                     <td>  
  41.                         <asp:Button ID="btnCompostiteDataTypeStringValue" runat="server" Text="Get Message From Service" OnClick="btnCompostiteDataTypeStringValue_Click"></asp:Button>  
  42.                     </td>  
  43.                     <td>  
  44.                         <asp:Label ID="lblCompostiteDataTypeStringValueFromService" runat="server"></asp:Label>  
  45.                     </td>  
  46.                 </tr>  
  47.             </table>  
  48.         </div>  
  49.     </form>  
  50. </body>  
  51. </html>  
The code behind of this page that is actually accessing the service via reflection is shown below, one can find inline comments in my code to understand what has been implemented and done via code.
 
Default.aspx.cs
  1. using System;  
  2. using System.CodeDom.Compiler;  
  3. using System.Collections.Generic;  
  4. using System.Collections.ObjectModel;  
  5. using System.Globalization;  
  6. using System.Linq;  
  7. using System.Reflection;  
  8. using System.ServiceModel;  
  9. using System.ServiceModel.Description;  
  10. public partial class _Default : System.Web.UI.Page  
  11. {  
  12.     protected void Page_Load(object sender, EventArgs e)  
  13.     {  
  14.     }  
  15.     protected void btnGetUserFromService_Click(object sender, EventArgs e)  
  16.     {  
  17.         CompilerResults compilerResults = null;  
  18.         //Create the proxy instance and returns it back, One parameter to this method is compiler compilerResults(Reference Type) this has been done so that the assemblies are   
  19.         //compiled only once, one can change the implementation adhering to coding guidelines and as per the implementation and requirement  
  20.         object proxyInstance = GetProxyInstance(ref compilerResults);  
  21.         string operationName = "GetTestString";  
  22.         // Get the operation's method  
  23.         var methodInfo = proxyInstance.GetType().GetMethod(operationName);  
  24.         //Paramaters if any required by the method are added into optional paramaters  
  25.         object[] operationParameters = new object[] { txtUser.Text };  
  26.         //Invoke Method, and get the return value  
  27.         lblUserMessage.Text = methodInfo.Invoke(proxyInstance, BindingFlags.InvokeMethod, null, operationParameters, null).ToString();  
  28.     }  
  29.     protected void btnCompostiteDataTypeStringValue_Click(object sender, EventArgs e)  
  30.     {  
  31.         CompilerResults compilerResults = null;  
  32.         //Create the proxy instance and returns it back, One parameter to this method is compiler compilerResults(Reference Type) this has been done so that the assemblies are   
  33.         //compiled only once, one can change the implementation adhering to coding guidelines and as per the implementation and requirement  
  34.         object proxyInstance = GetProxyInstance(ref compilerResults);  
  35.         string operationName = "GetTestDataUsingDataContract";  
  36.         // Get the operation's method  
  37.         var methodInfo = proxyInstance.GetType().GetMethod(operationName);  
  38.         //Here we are getting the method parameters, it has been done because the method parameter is a complex type, so we get the parameter, then its type and then finally  
  39.         //create an instance of it so that we can sent the instance to the service method by filling in its parameters  
  40.         ParameterInfo[] paramInfos = methodInfo.GetParameters();  
  41.         //As said we get the paramater type, we can also loop the paramaters if there are more than one parameter, but as of now i have only one.  
  42.         var parameterType = paramInfos[0].ParameterType;  
  43.         //Creating the instance of Paramater type  
  44.         var parameter = compilerResults.CompiledAssembly.CreateInstance(parameterType.FullName, false, BindingFlags.CreateInstance, nullnullnullnull);  
  45.         //Now we are setting the properties of that parameter by the value we want to set it before sending the request to the service, there can be multiple properties and  
  46.         //parameters, but as of now i know i want to fill in the paramater present at specified index, one can also make checks on the name of parameter or any other thing /  
  47.         //before making any assignment.  
  48.         parameterType.GetProperties()[1].SetValue(parameter, true);  
  49.         parameterType.GetProperties()[2].SetValue(parameter, txtCompostiteDataTypeStringValue.Text);  
  50.         //Composite data type parameter if any required by the method are added into optional paramaters  
  51.         object[] operationParameters = new object[] { parameter };  
  52.         //Invokes service method and get the result.  
  53.         var result = methodInfo.Invoke(proxyInstance, BindingFlags.InvokeMethod, null, operationParameters, null);  
  54.         //Now retreieving the result type, what i mean is i dont know in which composite type service is returning the result, so when we get the result, we retrieve its type  
  55.         //and then create the instance of that type.  
  56.         var resultType = methodInfo.Invoke(proxyInstance, BindingFlags.InvokeMethod, null, operationParameters, null).GetType();  
  57.         //Getting the values from the result properties and using them  
  58.         lblCompostiteDataTypeStringValueFromService.Text = resultType.GetProperties()[2].GetValue(result).ToString();  
  59.     }  
  60.     private object GetProxyInstance(ref CompilerResults compilerResults)  
  61.     {  
  62.         object proxyInstance = null;  
  63.         // Define the WSDL Get address, contract name and parameters, with this we can extract WSDL details any time  
  64.         Uri address = new Uri("http://localhost:64508/Service1.svc?wsdl");  
  65.         // For HttpGet endpoints use a Service WSDL address a mexMode of .HttpGet and for MEX endpoints use a MEX address and a mexMode of .MetadataExchange  
  66.         MetadataExchangeClientMode mexMode = MetadataExchangeClientMode.HttpGet;  
  67.         string contractName = "IService1";  
  68.         // Get the metadata file from the service.  
  69.         MetadataExchangeClient metadataExchangeClient = new MetadataExchangeClient(address, mexMode);  
  70.         metadataExchangeClient.ResolveMetadataReferences = true;  
  71.         //One can also provide credentials if service needs that by the help following two lines.  
  72.         //ICredentials networkCredential = new NetworkCredential("", "", "");  
  73.         //metadataExchangeClient.HttpCredentials = networkCredential;  
  74.         //Gets the meta data information of the service.  
  75.         MetadataSet metadataSet = metadataExchangeClient.GetMetadata();  
  76.         // Import all contracts and endpoints.  
  77.         WsdlImporter wsdlImporter = new WsdlImporter(metadataSet);  
  78.         //Import all contracts.  
  79.         Collection<ContractDescription> contracts = wsdlImporter.ImportAllContracts();  
  80.         //Import all end points.  
  81.         ServiceEndpointCollection allEndpoints = wsdlImporter.ImportAllEndpoints();  
  82.         // Generate type information for each contract.  
  83.         ServiceContractGenerator serviceContractGenerator = new ServiceContractGenerator();  
  84.         //Dictionary has been defined to keep all the contract endpoints present, contract name is key of the dictionary item.  
  85.         var endpointsForContracts = new Dictionary<string, IEnumerable<ServiceEndpoint>>();  
  86.         foreach (ContractDescription contract in contracts)  
  87.         {             
  88.          serviceContractGenerator.GenerateServiceContractType(contract);  
  89.             // Keep a list of each contract's endpoints.  
  90.             endpointsForContracts[contract.Name] = allEndpoints.Where(ep => ep.Contract.Name == contract.Name).ToList();  
  91.         }  
  92.         // Generate a code file for the contracts.  
  93.         CodeGeneratorOptions codeGeneratorOptions = new CodeGeneratorOptions();  
  94.         codeGeneratorOptions.BracingStyle = "C";  
  95.         // Create Compiler instance of a specified language.  
  96.         CodeDomProvider codeDomProvider = CodeDomProvider.CreateProvider("C#");  
  97.         // Adding WCF-related assemblies references as copiler parameters, so as to do the compilation of particular service contract.  
  98.         CompilerParameters compilerParameters = new CompilerParameters(new string[] { "System.dll""System.ServiceModel.dll""System.Runtime.Serialization.dll" });  
  99.         compilerParameters.GenerateInMemory = true;  
  100.         //Gets the compiled assembly.  
  101.         compilerResults = codeDomProvider.CompileAssemblyFromDom(compilerParameters, serviceContractGenerator.TargetCompileUnit);  
  102.         if (compilerResults.Errors.Count <= 0)  
  103.         {  
  104.             // Find the proxy type that was generated for the specified contract (identified by a class that implements the contract and ICommunicationbject - this is contract  
  105.             //implemented by all the communication oriented objects).  
  106.             Type proxyType = compilerResults.CompiledAssembly.GetTypes().First(t => t.IsClass && t.GetInterface(contractName) != null &&  
  107.             t.GetInterface(typeof(ICommunicationObject).Name) != null);  
  108.             // Now we get the first service endpoint for the particular contract.  
  109.             ServiceEndpoint serviceEndpoint = endpointsForContracts[contractName].First();  
  110.             // Create an instance of the proxy by passing the endpoint binding and address as parameters.  
  111.             proxyInstance = compilerResults.CompiledAssembly.CreateInstance(proxyType.Name, false, System.Reflection.BindingFlags.CreateInstance, null,  
  112.                 new object[] { serviceEndpoint.Binding, serviceEndpoint.Address }, CultureInfo.CurrentCulture, null);  
  113.         }  
  114.         return proxyInstance;  
  115.     }  
  116. }  
In the code above, what you need to change is the Address of the WCF Service, the Contract name and the method name if different.
 
Now we are good to go, open this default page and try using the WCF Service, you will see you will get the results from the service; also you can debug it at any point of time.
 
I am also attaching the sample running code, maybe you can download it and see the implementation working without making any effort. I have made this sample using Microsoft Visual Studio 2012/.Net Framework 4.5, although there will not be any issue in any of the frameworks on or above .Net 3.5.
 
Hope it helps.


Similar Articles