Shared Assemblies

As you've seen in this chapter, .NET Remoting applications need to share common information about remoteable types between server and client. Contrary to other remoting schemas like CORBA, Java RMI, and J2EE EJBs, with which you don't have a lot of choice for writing these shared interfaces, base classes, and metadata, the .NET Framework gives you at least four possible ways to do so, as I discuss in the following sections.

Shared Implementation

The first way to share information about remoteable types is to implement your server-side objects in a shared assembly and deploy this to the client as well. The main advantage here is that you don't have any extra work. Even though this might save you some time during implementation, I really recommend against this approach. Not only does it violate the core principles of distributed application development, but it also allows your clients, which are probably third parties accessing your ERP system to automate order entry, to use ILDASM or one of the upcoming MSIL-to-C# decompilers to disassemble and view your business logic. Unfortunately, this approach is shown in several MSDN examples.

Nevertheless, there are application scenarios that depend on this way of sharing the metadata. When you have an application that can be used either connected or disconnected and will access the same logic in both cases, this might be the way to go. You can then "switch" dynamically between using the local implementation and using the remote one.

Shared Interfaces

In the first examples in this book, I show the use of shared interfaces. With this approach, you create an assembly that is copied to both the server and the client. The assembly contains the interfaces that will be implemented by the server. The disadvantage to using this process of sharing the metadata is that you won't be able to pass those objects as parameters to functions running in a different context (either on the same or another server or on another client) because the resulting MarshalByRefObject cannot be downcast to these interfaces.

Shared Base Classes

Instead of sharing interfaces between the client and the server, you can also create abstract base classes in a shared assembly. The server-side object will inherit from these classes and implement the necessary functionality. The big advantage here is that abstract base classes are, contrary to the shared interfaces, capable of being passed around as parameters for methods located in different AppDomains. Still, this approach has one disadvantage: you won't be able to use those objects without Activator.GetObject() or a factory. Normally when the .NET Framework is configured correctly on the client side, it is possible to use the new operator to create a reference to a remote object. Unfortunately, you can never create a new instance of an abstract class or an interface, so the compiler will block this functionality.

SoapSuds-Generated Metadata

As each of the other approaches has a drawback, let's see what SoapSuds can do for you. This program's functionality is to extract the metadata (that is, the type definition) from a running server or an implementation assembly and generate a new assembly that contains only this meta information. You will then be able to reference this assembly in the client application without manually generating any intermediate shared assemblies.

Calling SoapSuds

SoapSuds is a command-line utility, therefore the easiest way to start it is to bring up the Visual Studio .NET Command Prompt by selecting Start Programs Microsoft Visual Studio .NET Visual Studio .NET Tools. This command prompt will have the path correctly set so that you can execute all .NET Framework SDK tools from any directory.

Starting SoapSuds without any parameters will give you detailed usage information. To generate a metadata DLL from a running server, you have to call SoapSuds with the -url parameter:

 soapsuds -url:<URL> -oa:<OUTPUTFILE>.DLL -nowp 
Note 

You normally have to append ?wsdl to the URL your server registered for a SOA to allow SoapSuds to extract the metadata.

To let SoapSuds extract the information from a compiled DLL, you use the -ia parameter:

 soapsuds -ia:<assembly> -oa:<OUTPUTFILE>.DLL -nowp 

Wrapped Proxies

When you run SoapSuds in its default configuration (without the -nowp parameter) by passing only a URL as an input parameter and telling it to generate an assembly, it will create what is called a wrapped proxy. The wrapped proxy can only be used on SOAP channels and will directly store the path to your server. Normally you do not want this.

Note 

This behavior is useful when you want to access a third-party Web Service whose application URL you happen to have.

I normally recommend using wrapped proxies only when you want to quickly test a SOAP remoting service. As an example, in the next section I show you how to implement a server without previously specifying any shared interfaces or base classes.

Implementing the Server

The server in this example will be implemented without any up-front definition of interfaces. You only need to create a simplistic SAO and register an HTTP channel to allow access to the metadata and the server-side object, as shown in Listing 3-22.

Listing 3-22: Server That Presents a SAO

start example
 using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels.Http; using System.Runtime.Remoting.Channels; namespace Server {    class SomeRemoteObject: MarshalByRefObject    {       public void doSomething()       {          Console.WriteLine("SomeRemoteObject.doSomething() called");       }    }    class ServerStartup    {       static void Main(string[] args)       {          Console.WriteLine ("ServerStartup.Main(): Server started");          HttpChannel chnl = new HttpChannel(1234);          ChannelServices.RegisterChannel(chnl);          RemotingConfiguration.RegisterWellKnownServiceType(                typeof(SomeRemoteObject),                "SomeRemoteObject.soap",                WellKnownObjectMode.SingleCall);          // the server will keep running until keypress.          Console.ReadLine();       }    } } 
end example

Generating the SoapSuds Wrapped Proxy

To generate a wrapped proxy assembly, use the SoapSuds command line shown in Figure 3-31. The resulting meta.dll should be copied to the client directory, as you will have to reference it when building the client-side application.

click to expand
Figure 3-31: SoapSuds command line used to generate a wrapped proxy

Implementing the Client

Assuming you now want to implement the client application, you first have to set a reference to the meta.dll in the project's References dialog box in VS .NET or employ the /r:meta.dll parameter to the command-line compiler. You can then use the Server namespace and directly instantiate a SomeRemoteObject using the new operator, as shown in Listing 3-23.

Listing 3-23: Wrapped Proxies Simplify the Client's Source Code

start example
 using System; using Server; namespace Client {    class Client    {       static void Main(string[] args)       {          Console.WriteLine("Client.Main(): creating rem. reference");          SomeRemoteObject obj = new SomeRemoteObject();          Console.WriteLine("Client.Main(): calling doSomething()");          obj.doSomething();          Console.WriteLine("Client.Main(): done ");          Console.ReadLine();       }    } } 
end example

Even though this code looks intriguingly simply, I recommend against using a wrapped proxy for several reasons: the server's URL is hard coded, and you can only use an HTTP channel and not a TCP channel.

When you start this client, it will generate the output shown in Figure 3-32. Check the server's output in Figure 3-33 to see that doSomething() has really been called on the server-side object.

click to expand
Figure 3-32: Client's output when using a wrapped proxy

click to expand
Figure 3-33: The server's output shows that doSomething() has been called.

Wrapped Proxy Internals

Starting SoapSuds with the parameter -gc instead of -oa:<assemblyname> will generate C# code in the current directory. You can use this code to manually compile a DLL or include it directly in your project.

Looking at the code in Listing 3-24 quickly reveals why you can use it without any further registration of channels or objects. (I strip the SoapType attribute, which would normally contain additional information on how to remotely call the object's methods.)

Listing 3-24: A SoapSuds-Generated Wrapped Proxy

start example
 using System; using System.Runtime.Remoting.Messaging; using System.Runtime.Remoting.Metadata; using System.Runtime.Remoting.Metadata.W3cXsd2001; namespace Server {    public class SomeRemoteObject :           System.Runtime.Remoting.Services.RemotingClientProxy    {       // Constructor       public SomeRemoteObject()       {           base.ConfigureProxy(this.GetType(),                "http://localhost:1234/SomeRemoteObject.soap");       }       public Object RemotingReference       {          get{return(_tp);}       } [SoapMethod(SoapAction="http://schemas.microsoft.com/clr/nsassem/ Server.SomeRemoteObject/Server#doSomething")]       public void doSomething()       {          ((SomeRemoteObject) _tp).doSomething();       }    } } 
end example

What this wrapped proxy does behind the scenes is provide a custom implementation/extension of RealProxy (which is the base for RemotingClientProxy) so that it can be used transparently. This architecture is shown in detail in Chapter 7.

Nonwrapped Proxy Metadata

Fortunately, SoapSuds allows the generation of nonwrapped proxy metadata as well. In this case, it will only generate empty class definitions, which can then be used by the underlying .NET Remoting TransparentProxy to generate the true method calls—no matter which channel you are using.

This approach also gives you the huge advantage of being able to use configuration files for channels, objects, and the corresponding URLs (more on this in the next chapter) so that you don't have to hard code this information. In the following example, you can use the same server as in the previous example, only changing the SoapSuds command and implementing the client in a different way.

Generating the Metadata with SoapSuds

As you want to generate a metadata-only assembly, you have to pass the -nowp parameter to SoapSuds to keep it from generating a wrapped proxy (see Figure 3-34).

click to expand
Figure 3-34: SoapSuds command line for a metadata-only assembly

Implementing the Client

When using metadata-only output from SoapSuds, the client looks a lot different from the previous one. In fact, it closely resembles the examples I show you at the beginning of this chapter.

First you have to set a reference to the newly generated meta.dll from the current SoapSuds invocation and indicate that your client will be using this namespace. You can then proceed with the standard approach of creating and registering a channel and calling Activator.GetObject() to create a reference to the remote object. This is shown in Listing 3-25.

Listing 3-25: The Client with a Nonwrapped Proxy

start example
 using System; using System.Runtime.Remoting; using System.Runtime.Remoting.Channels.Http; using System.Runtime.Remoting.Channels; using Server; namespace Client {    class Client    {       static void Main(string[] args)       {         HttpChannel chnl = new HttpChannel();         ChannelServices.RegisterChannel(chnl);          Console.WriteLine("Client.Main(): creating rem. reference");          SomeRemoteObject obj = (SomeRemoteObject) Activator.GetObject (                typeof(SomeRemoteObject),                "http://localhost:1234/SomeRemoteObject.soap");          Console.WriteLine("Client.Main(): calling doSomething()");          obj.doSomething();          Console.WriteLine("Client.Main(): done ");          Console.ReadLine();       }    } } 
end example

When this client is started, both the client-side and the server-side output will be the same as in the previous example (see Figures 3-35 and 3-36).

click to expand
Figure 3-35: The client's output when using a metadata-only assembly

click to expand
Figure 3-36: The server's output is the same as in the previous example.




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