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.”
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.
The value of the __MethodName key identifies the method as .ctor, which corresponds to the MyRemoteObject constructor method. Because MyRemoteObject’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.
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.
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, PublicKeyToken=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.
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.
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.