building a webservice proxy at runtime

In this post i will write about webservice proxies in c#. If you want to access a webservice in c# you can add a service referenc. If you add an URL to a Webservice Visual Studio generates you a c# proxy call. If you instanciate this class you can use the object to access the Webservice like any local object. That mechanism works very good but what if you want to generate this proxy at runtime and access this proxy with reflection? To do so we need runtime compilation and the ServiceDescription and ServiceDescriptionImporter classes. The first step i to get the wsdl definition.

Uri uri = new Uri("www.webservice.com/testservice.asmx");
WebRequest webRequest = WebRequest.Create(uri + "?WSDL");
webRequest.Credentials = CredentialCache.DefaultCredentials;
webRequest.ContentType = "application/x-www-form-urlencoded";
webRequest.Proxy = null;
webRequest.Timeout = -1;
webRequest.Method = "GET";
WebResponse webResponse;
webResponse = webRequest.GetResponse();
using (Stream stream = webResponse.GetResponseStream())
{
// here starts the generation
}

All we do here is to generate a http request to the wsdl description and generate a stream that contains the xml.

ServiceDescription description = ServiceDescription.Read(stream);

Now we read the stream into the ServiceDescription. The ServiceDescription object now knows the structure of the wsdl. After that we generate a ServiceDescriptionImporter:

ServiceDescriptionImporter descriptionImporter = new ServiceDescriptionImporter();
descriptionImporter.ProtocolName = "SOAP";
descriptionImporter.AddServiceDescription(description, null, null);
descriptionImporter.Style = ServiceDescriptionImportStyle.Client;
descriptionImporter.CodeGenerationOptions = CodeGenerationOptions.GenerateProperties;

We tell the ServiceDescriptionImporter that he should use SOAP to access the webservice, generate it at the client and generate simple types as properties. With the AddServiceDescription Method we add the information about the WSDL to the ServiceDescriptionImporter. Now we generate a CodeNamespace and a CodeCompileUnit and add the CodeNamespace to the CodeCompileUnit.

CodeNamespace codeNamespace = new CodeNamespace();
CodeCompileUnit codeCompileUnit = new CodeCompileUnit();
codeCompileUnit.Namespaces.Add(codeNamespace);

The next point is crucial. We now import the structure of our proxy from the ServiceDescriptionImporter and save it in the codeCompileUnit.

ServiceDescriptionImportWarnings serviceDescriptionImportWarnings =
descriptionImporter.Import(codeNamespace, codeCompileUnit);

Here we can check if the import is correct.

if (serviceDescriptionImportWarnings != ServiceDescriptionImportWarnings.NoCodeGenerated)
{
    // if code generation is ok we can generate the dynamic assembly with the proxy in it
}

Now we can generate the assembly with runtime compilation.

CodeDomProvider compiler = new CSharpCodeProvider();
string[] references = new[] { "System.Web.Services.dll", "System.Xml.dll" };
CompilerParameters parameters = new CompilerParameters(references);
CompilerResults results = compiler.CompileAssemblyFromDom(parameters, codeCompileUnit);

At this point the dynamic proxy definition is ready. The next step is to get the exported Types and generate an instance of the proxy.

Type[] assemblyTypes = results.CompiledAssembly.GetExportedTypes();
object webServiceProxy = Activator.CreateInstance(assemblyTypes[0]);

So the object webServiceProxy now contains the correct proxy, now we need to find the Methodes to call and call them. In this case i a searching for the “GetAge” method.

MethodInfo methodInfo = webServiceProxy.GetType().GetMethod("GetAge");
var result = methodInfo.Invoke(webServiceProxy, new object[] { "mathias" });

Thats it, as long as you can access the wsdl file you can generate a proxy for every webservice you want. Here is the whole code example.

Uri uri = new Uri("www.webservice.com/testservice.asmx");
WebRequest webRequest = WebRequest.Create(uri.ToString() + "?WSDL");
webRequest.Credentials = CredentialCache.DefaultCredentials;
webRequest.ContentType = "application/x-www-form-urlencoded";
webRequest.Proxy = null;
webRequest.Timeout = -1;
webRequest.Method = "GET";
WebResponse webResponse;
webResponse = webRequest.GetResponse();
using (Stream stream = webResponse.GetResponseStream())
{
    ServiceDescription description = ServiceDescription.Read(stream);
    ServiceDescriptionImporter descriptionImporter = new ServiceDescriptionImporter();
    descriptionImporter.ProtocolName = "SOAP";
    descriptionImporter.AddServiceDescription(description, null, null);
    descriptionImporter.Style = ServiceDescriptionImportStyle.Client;
    descriptionImporter.CodeGenerationOptions = CodeGenerationOptions.GenerateProperties;
    CodeNamespace codeNamespace = new CodeNamespace();
    CodeCompileUnit codeCompileUnit = new CodeCompileUnit();
    codeCompileUnit.Namespaces.Add(codeNamespace);
    ServiceDescriptionImportWarnings serviceDescriptionImportWarnings =
        descriptionImporter.Import(codeNamespace, codeCompileUnit);
    if (serviceDescriptionImportWarnings != ServiceDescriptionImportWarnings.NoCodeGenerated)
    {
        CodeDomProvider compiler = new CSharpCodeProvider();
        string[] references = new[] {"System.Web.Services.dll", "System.Xml.dll"};
        CompilerParameters parameters = new CompilerParameters(references);
        CompilerResults results = compiler.CompileAssemblyFromDom(parameters, codeCompileUnit);

        Type[] assemblyTypes = results.CompiledAssembly.GetExportedTypes();
        object webServiceProxy = Activator.CreateInstance(assemblyTypes[0]);

        MethodInfo methodInfo = webServiceProxy.GetType().GetMethod("GetAge");
        var result = methodInfo.Invoke(webServiceProxy, new object[] {"mathias"});
    }
}
Advertisements


If you have a note or a question please write a comment.

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s