Using Asynchronous Calls

In the .NET Framework, there are generally two ways of executing methods and processing their responses asynchronously:

  • Using a delegate's combination of BeginInvoke() and EndInvoke()

  • Using events (which implicitly use delegates)

For the most part, both are possible in .NET Remoting as well. Due to implementation differences, you nevertheless have to be careful when using asychronous method execution.

Using Delegates

Using a delegate's BeginInvoke()/EndInvoke() combination just simulates an asynchronous call, as the underlying connection will still be synchronous (when using one of the default HTTP or binary channels). This means that separate HTTP connections are made for each concurrently running asynchronous call.

Note 

According to the documentation, the clientConnectionLimit attribute, which can be set in the configuration file's <channel> entry for HTTP channel, should limit the number of simultaneous connections to a single server. This is not true in this case.

The Problem with Implementing Delegates

If you attempt to use delegates with objects from SoapSuds-generated DLLs, you will end up with TypeLoadExceptions, due to the way types are resolved in the remoting framework. Specifically, the .NET Framework tries to load the Type according to the SoapType attribute's XmlTypeNamespace property. This attribute is embedded by SoapSuds to allow for a correct server-side versioning of activation calls to CAOs, as seen previously.

You can observe this problem with the code shown in Listing 6-13, which will give the output shown in Figure 6-16 when used with a SoapSuds-generated DLL.

Listing 6-13: Trying to Use a Delegate with a SoapSuds-Generated Proxy

start example
 using System; using System.Collections; using System.Runtime.Remoting; using Server; namespace Client {    class Client    {       delegate void DoSomethingDelegate ();       static void Main(string[] args)       {          String filename = "client.exe.config";          RemotingConfiguration.Configure(filename);          SomeSAO sao = new SomeSAO();          // calling it synchronously to prove that          // everything's alright          Console.WriteLine("Will call synchronously");          sao.DoSomething();          Console.WriteLine("Synchronous call is ok");          DoSomethingDelegate del = new DoSomethingDelegate(sao.DoSomething);          try {             Console.WriteLine("BeginInvoke will be called");             IAsyncResult ar = del.BeginInvoke(null,null);             Console.WriteLine("EndInvoke will be called");             del.EndInvoke(ar);             Console.WriteLine("Invocation done");          } catch (Exception e) {             Console.WriteLine("EXCEPTION \n{0}",e.Message);          }          Console.ReadLine();       }    } } 
end example

click to expand
Figure 6-16: Using a delegate with a SoapSuds-generated DLL

Implementing a SoapSuds -gc Workaround

Several possible workarounds exist for this problem. When using only SAOs, for example, the SoapType attribute is not needed. You can therefore run soapsuds -ia:<assembly> -nowp -gc to generate C# code and manually remove this attribute, which will solve the delegate problem. Nevertheless, this doesn't work with CAOs!

You can see the output of soapsuds -ia:server -nowp -gc for a very simple SAO in Listing 6-14 (I inserted some line breaks to enhance the readability).

Listing 6-14: SoapSuds -gc Output for a Simple SAO

start example
 using System; using System.Runtime.Remoting.Messaging; using System.Runtime.Remoting.Metadata; using System.Runtime.Remoting.Metadata.W3cXsd2001; namespace Server {     [Serializable,      SoapType(SoapOptions=SoapOption.Option1|SoapOption.AlwaysIncludeTypes|      SoapOption.XsdString|SoapOption.EmbedAll,      XmlNamespace=@"http://schemas.microsoft.com/clr/nsassem/Server/Server%2C    %20Version%3D1.0.753.26188%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3Dnull",      XmlTypeNamespace=@"http://schemas.microsoft.com/clr/nsassem/Server/Serve    r%2C%20Version%3D1.0.753.26188%2C%20Culture%3Dneutral%2C%20PublicKeyToken%3Dn    ull")]      public class SomeSAO : System.MarshalByRefObject      {          [SoapMethod(SoapAction=@"http://schemas.microsoft.com/clr/nsassem/Server. SomeSAO/Server#DoSomething")]         public void DoSomething()         {             return;         }     } } 
end example

You can then simply add this source file to your Visual Studio .NET project and remove the complete SoapType attribute.

Note 

Don't forget to change the configuration file! The type property for the <wellknown> entry on the client side has to be changed from pointing to your generated_meta.dll (as in <wellknown type=“Server.SomeSAO, generated_meta” />) to include the name of the client-side exe (as in <wellknown type=“Server.SomeSAO, client” />).

The resulting source file without this attribute is shown in Listing 6-15:

Listing 6-15: The SoapSuds-Generated server.cs After Removing the Attribute

start example
 using System; using System.Runtime.Remoting.Messaging; using System.Runtime.Remoting.Metadata; using System.Runtime.Remoting.Metadata.W3cXsd2001; namespace Server {     [Serializable]     public class SomeSAO : System.MarshalByRefObject     {         [SoapMethod(SoapAction=@"http://schemas.microsoft.com/clr/nsassem/Server. SomeSAO/Server#DoSomething")]         public void DoSomething()         {              return;         }     } } 
end example

You can see the output of the previous client with the addition of this generated and changed proxy source in Figure 6-17.

click to expand
Figure 6-17: SAOs work asynchronously when removing the SoapType attribute.

Using Delegates with Wrapper Methods

Another possibility, which also works for CAOs, is to leave the XmlTypeAttribute intact, but to use a wrapper method in the client application that forwards the call to your remote object. This wrapper method is called asynchronously from the client application and renders the remoting call itself synchronous in regard to the wrapper method.

In this case, you can go back to using the SoapSuds-generated DLL from the first example, and you will only have to adopt the client to add another method that forwards the call, as shown in Listing 6-16.

Listing 6-16: The Client Using a Wrapper Function

start example
 using System; using System.Collections; using System.Runtime.Remoting; using Server; namespace Client {    class Client    {       delegate void DoSomethingDelegate (SomeSAO sao);       static void WrappedDoSomething(SomeSAO sao) {          // this method will just forward the call to the SAO          sao.DoSomething();       }       static void Main(string[] args)       {          String filename = "client.exe.config";          RemotingConfiguration.Configure(filename);          SomeSAO sao = new SomeSAO();          DoSomethingDelegate del = new DoSomethingDelegate(WrappedDoSomething);          try {             Console.WriteLine("BeginInvoke will be called");             IAsyncResult ar = del.BeginInvoke(sao, null,null);             Console.WriteLine("EndInvoke will be called");             del.EndInvoke(ar);             Console.WriteLine("Invocation done");          } catch (Exception e) {             Console.WriteLine("EXCEPTION \n{0}",e.Message);          }          Console.ReadLine();       }    } } 
end example

I have to admit that this is quite an unattractive workaround, as it turns the call into a synchronous remoting call. (Synchronous to the WrappedDoSomething() method, but still asynchronous in regard to void Main()!) Nevertheless, if you just want to call a single method asynchronously, this might be the right way to go without having to change too much of your SoapSuds-generated proxy code.




Advanced  .NET Remoting C# Edition
Advanced .NET Remoting (C# Edition)
ISBN: 1590590252
EAN: 2147483647
Year: 2002
Pages: 91
Authors: Ingo Rammer

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net