Microsoft .NET Remoting (Pro-Developer) - page 28

Summary

In this chapter, we discussed SOAP internals as they apply to .NET Remoting applications. Using this understanding of SOAP, we examined several message flows between the JobClient and JobServer applications. Although the .NET Framework does a great job of hiding the SOAP details from programmers performing higher-level tasks, understanding SOAP can give you a powerful tool for developing .NET Remoting applications. By watching SOAP traffic, you can see how the .NET Remoting infrastructure sends messages for constructing objects and renewing leases as well as viewing your own method-based messages on the wire. Because you now have a basic understanding of SOAP, we’ll use SOAP message flows in this book to reinforce concepts as appropriate.

Chapter 5

Messages and Proxies

So far, we’ve used only the out-of-the-box functionality of .NET Remoting. In this chapter, we’ll begin customizing various elements of the .NET Remoting infrastructure, starting with proxies. Specifically, we’ll look at creating a custom proxy that implements a simple load-balancing scheme. We’ll develop another proxy that shows how ProxyAttribute can be used to intercept activation. We’ll also show you how to use call context to transfer extra information with the method call. However, before we discuss customizing proxies, let’s examine the various kinds of messages a proxy might encounter.

Messages

You’ll see a lot of messages throughout the rest of this book because they’re the fundamental unit of data transfer in .NET Remoting applications. Because .NET Remoting objects such as proxies and message sinks use messages extensively, let’s discuss messages in more detail before we begin customizing those other objects.

Recall from Chapter 2, “Understanding the .NET Remoting Architecture,” that all .NET Remoting messages derive from IMessage. IMessage merely defines a single IDictionary property named Properties. IDictionary defines an interface for collections of key-and-value pairs. Because both the keys and values of IDictionary-based collections contain elements of type object, you can put any .NET type into these collections. But the objects you use must be serializable in order to be transported across a remoting boundary. We’ll look at serialization in depth in Chapter 8, “Serialization Formatters.”

Construction Call Messages

When you make a method call (including a constructor call) on a remote object, the .NET Remoting infrastructure constructs a message describing the method call. For example, consider the following client code:

Object obj = new MyRemoteObject();

The instantiation of the MyRemoteObject results in the instantiation of a .NET Remoting message with the IDictionary entries shown in Table 5-1.

Table 5-1. Construction Call Messages

Dictionary Key

Dictionary Value Data Type

Dictionary Value

__Uri

String

null

__MethodName

String

.ctor

__TypeName

System.String

MyNameSpace.MyRemoteObject, MyAssembly, Version=1.0.882.27668, CultureNeutral, PublicKeyToken=null

__MethodSignature

Type[]

null

__Args

Object[]

null

__CallContext

LogicalCallContext

null

__ActivationType

Type

null

__CallSiteActivationAttributes

Object[]

null

__ActivationType

Type

null

The value of the __MethodName key identifies the method as .ctor, which corresponds to the MyRemoteObject constructor method. Because MyRemote­Object’s constructor has no arguments, the __MethodSignature and __Args values are null. Because call context can be set in the client’s calling code, the dictionary contains a key named __CallContext. We’ll discuss call context and how to use it in the next section of this chapter. Finally, the message has dictionary keys for custom activation properties, which the .NET Remoting infrastructure uses during activation.

Method Call Messages

Consider the following method on the remote object:

Obj.MyMethod("A string", 14);

This method call results in the generation of a message with the IDictionary entries shown in Table 5-2.

Table 5-2. Method Call Messages

Dictionary Key

Dictionary Value Data Type

Dictionary Value

__Uri

String

null

__MethodName

String

MyMethod

__TypeName

System.String

MyNameSpace.MyRemoteObject, MyAssembly, Version=1.0.882.27668, CultureNeutral, Public­KeyToken=null

__MethodSignature

Type[]

[0] = System.String

[1] = System.Int32

__Args

Object[]

[0] = a string

[1] = 14

__CallContext

LogicalCallContext

null

Note that the __MethodName, __MethodSignature, and __Args keys are populated to reflect the remote object’s method. Because the object has already been activated, the activation keys aren’t present.

Message Types

All .NET Remoting messages implement the IMessage interface so that they at least have a single IDictionary property. The .NET Remoting infrastructure derives many interfaces from IMessage that generally serve two purposes. First, each of these interfaces provides various properties and/or methods to make accessing the IMessage internal dictionary more convenient. Second, each interface’s specific type serves as a marker so that you know how to handle the message. For example, you might want to differentiate between a message for constructing a client-activated object and a message generated from a regular method call. Although either of these messages could be conveyed via a simple IMessage class, by having different types the interfaces can inform you of the intent of a message. Table 5-3 summarizes the common message interfaces and classes that you might encounter.

Table 5-3. Common Remoting Message Types 

Message Type

Member Name

(Partial Listing)

Description

IMessage

Implemented by all .NET Remoting messages.

IMethodMessage

Implemented by all messages that describe stack-based methods. Specifies properties common to all methods.

Args

Array of method arguments passed.

MethodName

Name of the method that originated the message.

Uri

Uniform Resource Identifier (URI) of the object that this message is destined for.

TypeName

Full type name of the object that this message is destined for.

IMethodReturnMessage

Implemented by all messages returning to the client.

Exception

Exception thrown by the remote object, if any.

OutArgs

Array of [out] arguments.

ReturnValue

Object containing the return value of the remote method, if any.

IMethodCallMessage

Implemented by all messages originating from method calls. Specifies properties common to all method calls.

InArgs

Array of [in] arguments.

IConstructionCallMessage

Message implementing this interface is sent to a client-activated object when you call new or Activator.CreateInstance. Server-activated objects receive this message when you make the first message call. As its name implies, this is the first message sent to an object, and it specifies properties common to remote object construction.

ActivationType

Gets the type of the remote object to activate.

Activator

Gets or sets the activator used to activate the remote object.

ContextProperties

Gets the list of context properties that define the object’s creation context.

IConstructionReturnMessage

Message implementing this interface is sent back to the client in response to an IConstructionCallMessage.

ReturnMessage

Concrete class that implements IMethod­ReturnMessage. This class is documented so that you can conveniently construct your own return message if you want to intercept a method call to return a valid message without involving the remote object.

Now that we’ve discussed messages, we can start looking at the .NET Remoting objects that handle messages. We’ll start at the client side, where remote object method calls originate and the .NET Remoting infrastructure creates the messages.