Click here to Skip to main content
15,885,546 members
Articles / Programming Languages / C#
Article

How to handle events during a web service call?

Rate me:
Please Sign up or sign in to vote.
3.33/5 (17 votes)
3 Feb 20043 min read 67K   34   4
A C# class that allows to run web service asynchronous to handle events during them.

Introduction

Web Services are a cool thing. You can call them from any browser or other GUI, and since the Web Service Enhancement Kit is released, they don’t reduce you to a small amount of datatypes any more. They seem to fit for a real huge class of problems, but one critical question still isn’t that easy:

How to run them asynchronous to get more flexibility in the component that calls them?

Maybe you want to call a very slow web service and want to show a progress bar in your application showing the current state of action. Or you simply want to allow your application to run several routine jobs (triggered by timers or something) while the web service is running.

If you look into the proxy class of your webservices, you will find some methods that support asynchronous calls. For each web method, you find a corresponding Begin<webmethodname> and End<webmethodname> in it.

But how to use them easily without changing too many code in your application?

And how to prevent your application or the user running it from doing unpredictable things while the asynchronous webservice call?

Your application can only work correct if the return or out parameters of the web service have the final values, so what we need most of the time is a web service that runs synchronous to the application's workflow, but runs asynchronous regarding the amount of processor time it uses compared to the calling component. The calling component should not be blocked by the web service, so that it is able to manage its events while the web service is running.

The following C# class is a solution for this problem. It implements a “synchronous asynchronous webservice call”, that prevents your application from being blocked during web service calls.

Instead of calling the webservice directly, you have to call the static method CallWS of this class. You pass the in-parameters as a object[] and the out parameters as a ref object (which has to be an array inside, the problem is that .NET does not support ref object[], so we have to do it with a ref object that contains an array):

Let's look at a simple example with 3 in parameters and without out parameters:

C#
int intReturnValue = CAsyncWebService.CallWS(myWSObject, 
                              "GetData",new object[]{a,b,c});

myWSObject is an instance of your web service and a, b, c are the in parameters of some type.

The return value remains the return value and your webservice attachments are also still there. You can leave all code the same, expect the single line of the Web service call that turns into a single line call of the asynchronous webservice call (or a multi line call block, if you have out parameters)!

The only comfort you lose by this is that the compiler cannot check if the parameter types are matching the web service parameter types, but I don’t have any solution for this problem yet (the premium solution would be a code generator, that creates a different CallWS-method for each web method, but this is much oversized for our small problem here).

Let's look to a more complex call, that uses 3 out parameters in addition to the example above:

C#
object objOutParams = new object[]{d,e,f}; 
// d,e,f are out variables of some type

int intReturnValue = CAsyncWebService.CallWS(myWSObject, 
    "GetData",new object[]{a,b,c},ref objOutParams);
// d,e,f should contain other values now, but therefore
// we have to assign them back from the 
// objOutParams, because only their container was passed
// by ref, not the variables themselve 
// (.net does not support the combination "params ref object[] list") :

d =((object[])objOutParams)[0];
e =((object[])objOutParams)[1];
f =((object[])objOutParams)[2];

And now let's come to the code itself:

C#
using System;
using System.Reflection;
using System.Collections;
using System.Diagnostics;
 
namespace MyCoolTools
{
  /// <summary>
  /// This class implements a synchron asynchron webservice !!!
  /// Sounds crazy, doesn't it ?
  /// The Problem we wanted to solve is,
  /// we want to run several things within the gui
  /// while a webservice is running (update progressbar, polling for messages etc.)
  /// The User and the part of the businesslogic that does the webservice call 
  /// should not recognize, that the call is asnychron,
  /// because the user would do some
  /// unpredictable things in this case and
  /// the businesslogic that calls the webservice
  /// expects filled results after the webservice call
  /// is finished. so if we call it asynchron
  /// we have to implement a lot of additional stuff
  /// in the businesslogic to not getting wrong behaviour.
  /// We only want the gui having the chance to run its
  /// event handlers (timer-events or something)
  /// while a webservice is running ! This class is the solution.
  /// Instead calling the webservice directly
  /// you have to call the static Method CallWS of this class.
  /// you pass the in-parameters as a object[]
  /// and the out parameters as a ref object (which has
  /// to be an array inside, the problem is that .net 
  /// does not support ref object[] so we have to do it with
  /// a ref object that contains an array) :
  /// 
  /// object objOutParams = new object[]{d,e,f}; 
      // d,e,f are out variables of some type

  /// int intReturnValue = CAsyncWebService.CallWS(myWSObject,
  ///           "GetData",new object[]{a,b,c},ref objOutParams);
  /// // a,b,c are in-parameters of some type
  /// // d,e,f should contain other values now, but therefore
  /// we have to assign them back from the 
  /// // objOutParams, because only their container was
  ///    passed by ref, not the variables themselve 
  /// // (.net does not support the combination "params ref object[] list") :
  /// 
  /// d =((object[])objOutParams)[0];
  /// e =((object[])objOutParams)[1];
  /// f =((object[])objOutParams)[2];
  /// 
  /// the return value remains the return value and your
  /// webservice attachments are also still there. 
  /// you can leave all code the same, expect the single line
  /// of the Web service call that turns into
  /// a single line call of the async webservice call
  /// (or a multi line call block, if you have out parameters) !
  /// Uwe Arndt, (www.uwearndt.de) 03.02.04
  /// </summary>
  public class CAsyncWebService
  {
    private static bool m_blnWSRunning=false;
    private static object m_objWSResult=null;
    private static object m_objWSProxy=null;
    private static string m_strWebMethod=string.Empty;
    private static object m_WSOutParams=null;

    public static object CallWS(object wsProxy,string strWebMethod)
    {
      return CallWS(wsProxy,strWebMethod,new object[]{});
    }

    public static object CallWS(object wsProxy,
          string strWebMethod,object[] objWSParams)
    {
      object emptyOutArray = new object[]{};
      return CallWS(wsProxy,strWebMethod,objWSParams,ref emptyOutArray);
    }

    public static object CallWS(object wsProxy, 
          string strWebMethod,object[] objWSParams, 
          ref object objWSOutParams)
    {
      // set members from parameters, that the callback
      // method needs (we cant pass them to it
      // directly because the callback-method-interface is fixed)
      m_objWSProxy=wsProxy;
      m_strWebMethod=strWebMethod;
      m_WSOutParams=objWSOutParams;

      // read the url to call
      System.Net.WebRequest wrq = 
       System.Net.WebRequest.Create(InvokeGetPropertyOfPrivateObject(m_objWSProxy,
       "Url").ToString());
      System.AsyncCallback objAsyncCallBack = 
         new System.AsyncCallback(MyAsyncCallBack);

      // we have to append two parameters to the parameter
      // list, therefore we need a arraylist, 
      // because the normal array doesnt support "Add"
      // (i know the IList-Interface, but it doesnt
      // work here, because our other problem with the array is, that it is fixed)
      ArrayList al = new ArrayList();
      for(int i = 0;i<objWSParams.Length;i++)
      {
        al.Add(objWSParams[i]);
      }
      al.Add(objAsyncCallBack);
      al.Add(wrq);

      m_blnWSRunning=true;
      object objInParams=al.ToArray();
      InvokeMethodOfObject(m_objWSProxy,
         "Begin"+m_strWebMethod,ref objInParams);

      // wait until MyAsyncCallBack is called setting the m_blnWSRunning to false
      // the gui can do its events while this time
      while(m_blnWSRunning)
      {
        System.Windows.Forms.Application.DoEvents();
      }

      objWSOutParams=m_WSOutParams;
      return m_objWSResult;
    }

    private static void MyAsyncCallBack(IAsyncResult ar)
    {
      //add ar to parameter array
      ArrayList al = new ArrayList();
      al.Add(ar);
      for(int i = 0;i<((object[])m_WSOutParams).Length;i++)
      {
        al.Add(((object[])m_WSOutParams)[i]);
      }

      m_WSOutParams = al.ToArray();
      m_objWSResult = InvokeMethodOfObject(m_objWSProxy, 
                     "End"+m_strWebMethod,ref m_WSOutParams);

      // delete ar from returned parameter array
      al.Clear();
      for(int i = 1;i<((object[])m_WSOutParams).Length;i++)
      {
        al.Add(((object[])m_WSOutParams)[i]);
      }
      m_WSOutParams= al.ToArray();

      m_blnWSRunning=false;
    }

    // invokes a property of a private object,
    // that means an object, which you can see
    // in the debugger, but to whom you can not cast to,
    // because its type isnt visible at compiletime
    // returns the return value of the property
    private static object 
      InvokeGetPropertyOfPrivateObject(object objPrivate,
      string strProperty)
    {
      object o = null;
      try
      {
        System.Type st = ((object)objPrivate).GetType();
        PropertyInfo pi = st.GetProperty(strProperty);
        MethodInfo mi = pi.GetGetMethod();
        o = mi.Invoke(objPrivate,new object[0]);
      }
      catch(Exception ex)
      {
        Debug.WriteLine(ex.Message);
      }
      return o;
    }

    // invokes a method of the given object and returns its return value
    private static object InvokeMethodOfObject(object obj, 
                 string strMethod,ref object arMethodParams)
    {
      object o = null;
      try
      {
        object[] objParams = (object[])arMethodParams;
        System.Type st = ((object)obj).GetType();
        o = st.InvokeMember(strMethod,BindingFlags.DeclaredOnly | 
              BindingFlags.Public | BindingFlags.NonPublic | 
              BindingFlags.Instance | BindingFlags.InvokeMethod, 
              null,obj,objParams);
      }
      catch(Exception ex)
      {
        Debug.WriteLine(ex.Message);
      }
      return o;
    }
  }
}

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Germany Germany
I work as a freelancer software developer for several companies, mainly in microsoft environments.

At the moment i am working on a network management system in .net with SAP-Connection for the Deutsche Börse Systems AG in Frankfurt/Main.

My main interests besides .net and the outcoming Yukon-SQL-Server are chess and table tennis.

You can find more information on my Web-Site :

www.uwearndt.de

If you have any suggestions or remarks to my articles, please write to mail@uwearndt.de

Comments and Discussions

 
GeneralMy vote of 1 Pin
Bill SerGio, The Infomercial King30-Jun-10 15:02
Bill SerGio, The Infomercial King30-Jun-10 15:02 
QuestionHow to use it properly Pin
mwitlox16-Jun-06 2:03
mwitlox16-Jun-06 2:03 
QuestionHow to fire an event from WebService itself? Pin
vrushaliD31-Mar-05 23:37
vrushaliD31-Mar-05 23:37 
GeneralArticle Template Pin
Mazdak5-Feb-04 20:48
Mazdak5-Feb-04 20:48 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.