Home » Technology

Dynamically access .Net web service using .NET Compile Unit classes

17 June 2009 One Comment

Having a web service that published is very handy way to integrate one system with another. As number of available web service increased, sometime we would need a handy tool also that allow us to dynamically call web service within our code. This blog will discuss how we can do that using .NET 2.0 basic classes

Few weeks back, my team received a requirement to automatically send email to specific person to remind them if an equipment inspection record will be expired, this request actually wasn’t first request we have received, there were more request with same approach to send email or do something based on specific event. What we normally do to complete this is by creating a single service that run periodically to perform the task, however, as number of request increased and when I do server audit I found lots of tiny service like this running on our server. So we decide we gonna have a single service program that will run every hour and this service program will read XML file that will lists web service information need to be invoked.

To complete above scenario we would need a generic wrapper that able to read web service, reflect them and run it as required. Luckily .NET provide base classed called System.Web.Services.Description and System.Reflection.CompileUnit that we can utilize for this :) So based on that classed we build another class that we called as WebServiceInvoker, it only contains one function called Invoke to invoke specific web service.

Connect to Web Service

        '-- connect to web service and load service description
        If Not Credential Is Nothing Then client.Credentials = Credential
        Dim stream As Stream = client.OpenRead(String.Format("{0}?wsdl", Url))
        Dim description As ServiceDescription = ServiceDescription.Read(stream)
        '-- initialize service description importer
        Dim importer As ServiceDescriptionImporter = New ServiceDescriptionImporter()
        importer.ProtocolName = "Soap12"
        importer.AddServiceDescription(description, Nothing, Nothing)
        importer.Style = ServiceDescriptionImportStyle.Client
        importer.CodeGenerationOptions = Xml.Serialization.CodeGenerationOptions.GenerateProperties

Above code snippet used to read Wsdl entry of webservice going to be invoked. ServiceDescription class will read that entry and with help from ServiceDescriptionImporter class to store information that being read.

Prepare compile unit and compile ws-code into assembly

        Dim warning As ServiceDescriptionImportWarnings = importer.Import(nmspace, compileUnit)
        If warning = 0 Then
            Dim provider As CodeDom.Compiler.CodeDomProvider = CodeDom.Compiler.CodeDomProvider.CreateProvider("VB")
            Dim assyRefs() As String = {"System.dll", "System.Web.Services.dll", "System.Web.dll", "System.Xml.dll", "System.Data.dll"}
            Dim parms As New CodeDom.Compiler.CompilerParameters(assyRefs)
            Dim results As CodeDom.Compiler.CompilerResults = provider.CompileAssemblyFromDom(parms, compileUnit)

In this section we will prepare CodeDomProvider class, attach reference and compile all code unit that already sync into DOM information.

Run the web-service compile result

            '-- no error, execute then
            Dim wsClass As Object = results.CompiledAssembly.CreateInstance(ServiceName)
            If Not Credential Is Nothing Then wsClass.Credentials = Credential
            Dim mi As MethodInfo = wsClass.GetType().GetMethod(MethodName)
            ret = mi.Invoke(wsClass, args)

Final step is to run the webservice :)

Full code of WebServiceInvoker will be as follow:

   1:  Imports System.Net
   2:  Imports System.IO
   3:  Imports System.Web.Services
   4:  Imports System.Web.Services.Description
   5:  Imports System.Reflection
   6:  
   7:  Public Class WebServiceInvoker
   8:  
   9:      Public Function Invoke(ByVal Url As String, _
  10:                             ByVal ServiceName As String, ByVal MethodName As String, _
  11:                             ByVal Credential As ICredentials, ByVal args() As Object) As Object
  12:          Dim ret As Object = Nothing
  13:  
  14:          Dim client As New WebClient
  15:  
  16:          '-- connect to web service and load service description
  17:          If Not Credential Is Nothing Then client.Credentials = Credential
  18:          Dim stream As Stream = client.OpenRead(String.Format("{0}?wsdl", Url))
  19:          Dim description As ServiceDescription = ServiceDescription.Read(stream)
  20:  
  21:          '-- initialize service description importer
  22:          Dim importer As ServiceDescriptionImporter = New ServiceDescriptionImporter()
  23:          importer.ProtocolName = "Soap12"
  24:          importer.AddServiceDescription(description, Nothing, Nothing)
  25:          importer.Style = ServiceDescriptionImportStyle.Client
  26:          importer.CodeGenerationOptions = Xml.Serialization.CodeGenerationOptions.GenerateProperties
  27:  
  28:          '-- prepare DOM and CompileUnit for service we would like to invoke
  29:          Dim nmspace As New CodeDom.CodeNamespace()
  30:          Dim compileUnit As New CodeDom.CodeCompileUnit()
  31:          compileUnit.Namespaces.Add(nmspace)
  32:  
  33:          '-- import service from based on DOM Information into CompileUnit
  34:          Dim warning As ServiceDescriptionImportWarnings = importer.Import(nmspace, compileUnit)
  35:          If warning = 0 Then
  36:              Dim provider As CodeDom.Compiler.CodeDomProvider = CodeDom.Compiler.CodeDomProvider.CreateProvider("VB")
  37:              Dim assyRefs() As String = {"System.dll", "System.Web.Services.dll", "System.Web.dll", "System.Xml.dll", "System.Data.dll"}
  38:              Dim parms As New CodeDom.Compiler.CompilerParameters(assyRefs)
  39:              Dim results As CodeDom.Compiler.CompilerResults = provider.CompileAssemblyFromDom(parms, compileUnit)
  40:  
  41:              '-- handling error during compilation
  42:              If results.Errors.Count > 0 Then
  43:                  For Each err As CodeDom.Compiler.CompilerError In results.Errors
  44:                  Next
  45:                  Throw New System.Exception("Compile Error Occured calling webservice. Check Debug ouput window.")
  46:              End If
  47:  
  48:              '-- no error, execute then
  49:              Dim wsClass As Object = results.CompiledAssembly.CreateInstance(ServiceName)
  50:              If Not Credential Is Nothing Then wsClass.Credentials = Credential
  51:              Dim mi As MethodInfo = wsClass.GetType().GetMethod(MethodName)
  52:              ret = mi.Invoke(wsClass, args)
  53:          Else
  54:              ret = Nothing
  55:          End If
  56:  
  57:          Return ret
  58:      End Function
  59:  
  60:  End Class

Bookmark and Share

Related article(s)

If you think this post is interesting, maybe you would consider to subscribe to my feed by click here or keep update with my newest post via email

One Comment »

Leave your response!

Add your comment below, or trackback from your own site. You can also subscribe to these comments via RSS.

Be nice. Keep it clean. Stay on topic. No spam.

You can use these tags:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

This is a Gravatar-enabled weblog. To get your own globally-recognized-avatar, please register at Gravatar.