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:
- GetTestString: It expects a parameter of string type and returns a string.
- 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
- [ServiceContract]
- public interface IService1
- {
- [OperationContract]
- string GetTestString(string value);
-
- [OperationContract]
- CompositeType GetTestDataUsingDataContract(CompositeType composite);
- }
Default Composite Data Type you will automatically get is:
- [DataContract]
- public class CompositeType
- {
- bool boolValue = true;
- string stringValue = "Hello ";
- [DataMember]
- public bool BoolValue
- {
- get { return boolValue; }
- set { boolValue = value; }
- }
- [DataMember]
- public string StringValue
- {
- get { return stringValue; }
- set { stringValue = value; }
- }
- }
The Service class Service1.cs looks something like this:
- public class Service1 : IService1
- {
- public string GetTestString(string value)
- {
- return string.Format("Welcome {0}", value);
- }
- public CompositeType GetTestDataUsingDataContract(CompositeType composite)
- {
- if (composite.BoolValue)
- {
- composite.StringValue += " From service";
- }
- return composite;
- }
- }
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
- <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
- <!DOCTYPE html>
- <html xmlns="http://www.w3.org/1999/xhtml">
- <head runat="server">
- <title></title>
- </head>
- <body>
- <form id="form1" runat="server">
- <div>
- <table>
- <tr>
- <td>
- <asp:Label runat="server" Text="User"></asp:Label>
- </td>
- <td>
- <asp:TextBox ID="txtUser" runat="server"></asp:TextBox>
- </td>
- </tr>
- <tr>
- <td>
- <asp:Button ID="btnGetUserFromService" runat="server" Text="Get User Message" OnClick="btnGetUserFromService_Click"></asp:Button>
- </td>
- <td>
- <asp:Label ID="lblUserMessage" runat="server"></asp:Label>
- </td>
- </tr>
- </table>
- </div>
- <div>
- <table>
- <tr>
- <td>
- <asp:Label ID="lblCompostiteDataTypeStringValue" runat="server" Text="Enter String Value which will go to service in a composite data type"></asp:Label>
- </td>
- <td>
- <asp:TextBox ID="txtCompostiteDataTypeStringValue" runat="server"></asp:TextBox>
- </td>
- </tr>
- <tr>
- <td>
- <asp:Button ID="btnCompostiteDataTypeStringValue" runat="server" Text="Get Message From Service" OnClick="btnCompostiteDataTypeStringValue_Click"></asp:Button>
- </td>
- <td>
- <asp:Label ID="lblCompostiteDataTypeStringValueFromService" runat="server"></asp:Label>
- </td>
- </tr>
- </table>
- </div>
- </form>
- </body>
- </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
- using System;
- using System.CodeDom.Compiler;
- using System.Collections.Generic;
- using System.Collections.ObjectModel;
- using System.Globalization;
- using System.Linq;
- using System.Reflection;
- using System.ServiceModel;
- using System.ServiceModel.Description;
- public partial class _Default : System.Web.UI.Page
- {
- protected void Page_Load(object sender, EventArgs e)
- {
- }
- protected void btnGetUserFromService_Click(object sender, EventArgs e)
- {
- CompilerResults compilerResults = null;
-
-
- object proxyInstance = GetProxyInstance(ref compilerResults);
- string operationName = "GetTestString";
-
- var methodInfo = proxyInstance.GetType().GetMethod(operationName);
-
- object[] operationParameters = new object[] { txtUser.Text };
-
- lblUserMessage.Text = methodInfo.Invoke(proxyInstance, BindingFlags.InvokeMethod, null, operationParameters, null).ToString();
- }
- protected void btnCompostiteDataTypeStringValue_Click(object sender, EventArgs e)
- {
- CompilerResults compilerResults = null;
-
-
- object proxyInstance = GetProxyInstance(ref compilerResults);
- string operationName = "GetTestDataUsingDataContract";
-
- var methodInfo = proxyInstance.GetType().GetMethod(operationName);
-
-
- ParameterInfo[] paramInfos = methodInfo.GetParameters();
-
- var parameterType = paramInfos[0].ParameterType;
-
- var parameter = compilerResults.CompiledAssembly.CreateInstance(parameterType.FullName, false, BindingFlags.CreateInstance, null, null, null, null);
-
-
-
- parameterType.GetProperties()[1].SetValue(parameter, true);
- parameterType.GetProperties()[2].SetValue(parameter, txtCompostiteDataTypeStringValue.Text);
-
- object[] operationParameters = new object[] { parameter };
-
- var result = methodInfo.Invoke(proxyInstance, BindingFlags.InvokeMethod, null, operationParameters, null);
-
-
- var resultType = methodInfo.Invoke(proxyInstance, BindingFlags.InvokeMethod, null, operationParameters, null).GetType();
-
- lblCompostiteDataTypeStringValueFromService.Text = resultType.GetProperties()[2].GetValue(result).ToString();
- }
- private object GetProxyInstance(ref CompilerResults compilerResults)
- {
- object proxyInstance = null;
-
- Uri address = new Uri("http://localhost:64508/Service1.svc?wsdl");
-
- MetadataExchangeClientMode mexMode = MetadataExchangeClientMode.HttpGet;
- string contractName = "IService1";
-
- MetadataExchangeClient metadataExchangeClient = new MetadataExchangeClient(address, mexMode);
- metadataExchangeClient.ResolveMetadataReferences = true;
-
-
-
-
- MetadataSet metadataSet = metadataExchangeClient.GetMetadata();
-
- WsdlImporter wsdlImporter = new WsdlImporter(metadataSet);
-
- Collection<ContractDescription> contracts = wsdlImporter.ImportAllContracts();
-
- ServiceEndpointCollection allEndpoints = wsdlImporter.ImportAllEndpoints();
-
- ServiceContractGenerator serviceContractGenerator = new ServiceContractGenerator();
-
- var endpointsForContracts = new Dictionary<string, IEnumerable<ServiceEndpoint>>();
- foreach (ContractDescription contract in contracts)
- {
- serviceContractGenerator.GenerateServiceContractType(contract);
-
- endpointsForContracts[contract.Name] = allEndpoints.Where(ep => ep.Contract.Name == contract.Name).ToList();
- }
-
- CodeGeneratorOptions codeGeneratorOptions = new CodeGeneratorOptions();
- codeGeneratorOptions.BracingStyle = "C";
-
- CodeDomProvider codeDomProvider = CodeDomProvider.CreateProvider("C#");
-
- CompilerParameters compilerParameters = new CompilerParameters(new string[] { "System.dll", "System.ServiceModel.dll", "System.Runtime.Serialization.dll" });
- compilerParameters.GenerateInMemory = true;
-
- compilerResults = codeDomProvider.CompileAssemblyFromDom(compilerParameters, serviceContractGenerator.TargetCompileUnit);
- if (compilerResults.Errors.Count <= 0)
- {
-
-
- Type proxyType = compilerResults.CompiledAssembly.GetTypes().First(t => t.IsClass && t.GetInterface(contractName) != null &&
- t.GetInterface(typeof(ICommunicationObject).Name) != null);
-
- ServiceEndpoint serviceEndpoint = endpointsForContracts[contractName].First();
-
- proxyInstance = compilerResults.CompiledAssembly.CreateInstance(proxyType.Name, false, System.Reflection.BindingFlags.CreateInstance, null,
- new object[] { serviceEndpoint.Binding, serviceEndpoint.Address }, CultureInfo.CurrentCulture, null);
- }
- return proxyInstance;
- }
- }
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.