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

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

 

        //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

        //compiled only once, one can change the implementation adhering to coding guidelines and as per the implementation and requirement

        object proxyInstance = GetProxyInstance(ref compilerResults);

        string operationName = "GetTestString";

 

        // Get the operation's method

        var methodInfo = proxyInstance.GetType().GetMethod(operationName);

 

        //Paramaters if any required by the method are added into optional paramaters

        object[] operationParameters = new object[] { txtUser.Text };

 

        //Invoke Method, and get the return value

        lblUserMessage.Text = methodInfo.Invoke(proxyInstance, BindingFlags.InvokeMethod, null, operationParameters, null).ToString();

 

    }

 

    protected void btnCompostiteDataTypeStringValue_Click(object sender, EventArgs e)

    {

        CompilerResults compilerResults = null;

 

        //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

        //compiled only once, one can change the implementation adhering to coding guidelines and as per the implementation and requirement

        object proxyInstance = GetProxyInstance(ref compilerResults);

 

        string operationName = "GetTestDataUsingDataContract";

 

        // Get the operation's method

        var methodInfo = proxyInstance.GetType().GetMethod(operationName);

 

        //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

        //create an instance of it so that we can sent the instance to the service method by filling in its parameters

        ParameterInfo[] paramInfos = methodInfo.GetParameters();

 

        //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.

        var parameterType = paramInfos[0].ParameterType;

 

        //Creating the instance of Paramater type

        var parameter = compilerResults.CompiledAssembly.CreateInstance(parameterType.FullName, false, BindingFlags.CreateInstance, null, null, null, null);

 

        //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

        //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 /

        //before making any assignment.

        parameterType.GetProperties()[1].SetValue(parameter, true);

        parameterType.GetProperties()[2].SetValue(parameter, txtCompostiteDataTypeStringValue.Text);

 

        //Composite data type parameter if any required by the method are added into optional paramaters

        object[] operationParameters = new object[] { parameter };

 

        //Invokes service method and get the result.

        var result = methodInfo.Invoke(proxyInstance, BindingFlags.InvokeMethod, null, operationParameters, null);

 

        //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

        //and then create the instance of that type.

        var resultType = methodInfo.Invoke(proxyInstance, BindingFlags.InvokeMethod, null, operationParameters, null).GetType();

 

        //Getting the values from the result properties and using them

        lblCompostiteDataTypeStringValueFromService.Text = resultType.GetProperties()[2].GetValue(result).ToString();

 

    }

 

    private object GetProxyInstance(ref CompilerResults compilerResults)

    {

        object proxyInstance = null;

 

        // Define the WSDL Get address, contract name and parameters, with this we can extract WSDL details any time

        Uri address = new Uri("http://localhost:64508/Service1.svc?wsdl");

        // 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

        MetadataExchangeClientMode mexMode = MetadataExchangeClientMode.HttpGet;

        string contractName = "IService1";

 

        // Get the metadata file from the service.

        MetadataExchangeClient metadataExchangeClient = new MetadataExchangeClient(address, mexMode);

        metadataExchangeClient.ResolveMetadataReferences = true;

 

        //One can also provide credentials if service needs that by the help following two lines.

        //ICredentials networkCredential = new NetworkCredential("", "", "");

        //metadataExchangeClient.HttpCredentials = networkCredential;

 

        //Gets the meta data information of the service.

        MetadataSet metadataSet = metadataExchangeClient.GetMetadata();

 

        // Import all contracts and endpoints.

        WsdlImporter wsdlImporter = new WsdlImporter(metadataSet);

 

        //Import all contracts.

        Collection<ContractDescription> contracts = wsdlImporter.ImportAllContracts();

 

        //Import all end points.

        ServiceEndpointCollection allEndpoints = wsdlImporter.ImportAllEndpoints();

 

        // Generate type information for each contract.

        ServiceContractGenerator serviceContractGenerator = new ServiceContractGenerator();

 

        //Dictinary has been defined to keep all the contract endpoints present, contract name is key of the dictionary item.

        var endpointsForContracts = new Dictionary<string, IEnumerable<ServiceEndpoint>>();

 

        foreach (ContractDescription contract in contracts)

        {

            serviceContractGenerator.GenerateServiceContractType(contract);

            // Keep a list of each contract's endpoints.

            endpointsForContracts[contract.Name] = allEndpoints.Where(ep => ep.Contract.Name == contract.Name).ToList();

        }

 

        // Generate a code file for the contracts.

        CodeGeneratorOptions codeGeneratorOptions = new CodeGeneratorOptions();

        codeGeneratorOptions.BracingStyle = "C";

 

        // Create Compiler instance of a specified language.

        CodeDomProvider codeDomProvider = CodeDomProvider.CreateProvider("C#");

 

        // Adding WCF-related assemblies references as copiler parameters, so as to do the compilation of particular service contract.

        CompilerParameters compilerParameters = new CompilerParameters(new string[] { "System.dll", "System.ServiceModel.dll", "System.Runtime.Serialization.dll" });

        compilerParameters.GenerateInMemory = true;

 

        //Gets the compiled assembly.

        compilerResults = codeDomProvider.CompileAssemblyFromDom(compilerParameters, serviceContractGenerator.TargetCompileUnit);

 

        if (compilerResults.Errors.Count <= 0)

        {

            // Find the proxy type that was generated for the specified contract (identified by a class that implements the contract and ICommunicationbject - this is contract

            //implemented by all the communication oriented objects).

            Type proxyType = compilerResults.CompiledAssembly.GetTypes().First(t => t.IsClass && t.GetInterface(contractName) != null &&

                t.GetInterface(typeof(ICommunicationObject).Name) != null);

 

            // Now we get the first service endpoint for the particular contract.

            ServiceEndpoint serviceEndpoint = endpointsForContracts[contractName].First();

 

            // Create an instance of the proxy by passing the endpoint binding and address as parameters.

            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.

Up Next
    Ebook Download
    View all
    Learn
    View all