Remotable Objects

Remotable objects are objects that function well in a widely distributed environment. There are two main kinds of remotable objects:

  • Marshal-by-value objects, which are copied and passed out of the application domain.
  • Marshal-by-reference objects, for which a proxy is created and used by the client to access the object remotely.

Marshal-By-Value Objects

Marshal-by-value (MBV) objects declare their serialization rules (either by implementing ISerializable to implement their own serialization, or by being decorated with SerializableAttribute, which tells the system to serialize the object automatically) but do not ** extend MarshalByRefObject. The remoting system makes a complete copy of these objects and passes the copy to the calling application domain. Once the copy is in the caller's application domain, calls to the copy go directly to that copy. Further, MBV objects that are passed as arguments are also passed by value. Other than declaring the SerializableAttribute attribute or implementing ISerializable, you do not need to do anything to pass instances of your class by value across application or context boundaries.

Note   Starting with version 1.1 of the .NET Framework, the remoting infrastructure does not automatically deserialize certain types on the server. If this is true for your scenario, you must set the server's deserialization level to Full before the server will be able to deserialize and use your MBV object. For details, see Automatic Deserialization in .NET Remoting.

Use MBV objects when it makes sense for performance or processing reasons to move the complete state of the object and any executable functionality to the target application domain. In many scenarios, this reduces lengthy, resource-consuming round trips across network, process, and application domain boundaries. MBV objects are also used directly from within the object's original application domain. In this case, because no marshaling takes place, no copy is made and access is very efficient.

On the other hand, if your published objects are very large, passing an entire copy around a busy network might not be the best choice for your application. In addition, no changes to the state of the copied object are ever communicated back to the original object in the originating application domain. At an abstract level, this scenario is similar to that of a static HTML page requested by a client browser. The server copies the file, writes it into a stream, sends it out, and forgets about it. Any subsequent request is merely another request for another copy.

The remoting system makes extensive use of serializable objects. A reference to an object in another application domain, represented in the remoting system by the ObjRef class, is itself serializable; it must be possible to copy it exactly and send the copy to a request. Message objects that implement IMessage because they are the generic containers of call information and any other necessary object references. In addition, objects that simply transfer data are often serializable objects. DataSet, for example, extends MarshalByValueComponent, which implements ISerializable.

Remoting User-Defined Exceptions

System-defined exceptions are all marshal-by-value types (they implement the ISerializable interface) that, when thrown by a remote object, will be automatically copied to the caller if the remoting configurations permit. (Starting with version 1.1 of the .NET Framework, the <customErrors> element must be set to off to enable exceptions to flow to the caller.)

To create your own exception types that can be thrown by remote objects and caught by remote callers, you must do the following:

  1. Implement ISerializable.
  2. Place the SerializableAttribute attribute on the class.
  3. Implement a deserialization constructor that takes a SerializationInfo object and a StreamingContext object as parameters.

The following C# code example provides a simple implementation that, if configured properly, is copied back to the caller when thrown by the remote server object.

[Serializable]
public class CustomRemotableException : RemotingException, ISerializable {
   private string _internalMessage;

   public CustomRemotableException(){
      _internalMessage = String.Empty;
   }
   
   public CustomRemotableException(string message){
      _internalMessage = message;
   }
   
   public CustomRemotableException(SerializationInfo info, StreamingContext context){
      _internalMessage = (string)info.GetValue("_internalMessage", typeof(string));
   }

   public override void GetObjectData(SerializationInfo info, StreamingContext context){
      info.AddValue("_internalMessage", _internalMessage);
   }

   // Returns the exception information. 
   public override string Message{
      get {
            return "This is your custom remotable exception returning: \"" 
      + _internalMessage 
      + "\"";
      }
   }
}

Marshal-By-Reference Objects

Marshal-by-reference (MBR) objects are remotable objects that extend at least System.MarshalByRefObject. Depending on what type of activation has been declared, when a client creates an instance of an MBR object in its own application domain, the .NET remoting infrastructure creates a proxy object in the caller's application domain that represents the MBR object, and returns to the caller a reference to that proxy. The client then makes calls on the proxy. Remoting marshals those calls, sends them back to the originating application domain, and invokes the call on the actual object.

Note   If the client is in the same application domain as the MBR object, the infrastructure returns to the client a direct reference to the MBR object, avoiding the overhead of marshaling.

If a MarshalByRefObject is passed as a parameter, it becomes a proxy in the other application domain when the call arrives. MBR return values and out parameters work in the same way.

Note   Starting with version 1.1 of the .NET Framework, the remoting infrastructure does not automatically deserialize certain types on the server. For example, to obtain support for MBR objects passed as parameters, you must set the server's deserialization level to Full before the server will be able to deserialize and use the MBR parameter. For details about this and other scenarios, see Automatic Deserialization in .NET Remoting.

You should use MBR objects when the state of the object and any executable functionality should stay in the application domain in which it was created. For example, an object that has an internal field that is an operating system handle should extend MarshalByRefObject because the operating system handle would not be meaningful in another application domain, in another process, or on another computer. Sometimes an object can also be prohibitively large; that might work on a robust server, but not when sent over a wire to a 33.6 kbps modem.

Context-Bound Objects

Context-bound objects are MBR objects that inherit from System.ContextBoundObject, which itself inherits from System.MarshalByRefObject. You can think of a context as a subdivision of an application domain that provides a rich environment for the objects residing in it during execution. For example, a context could guarantee that the objects will not be accessed simultaneously by multiple threads. Every application domain has a default context. Most managed code creates objects and calls members directly from within the same application domain using that domain's default context, with no context-related issues. All types that inherit from ContextBoundObject are exposed to other contexts (in the same domain or in other domains) as proxies.

For example, suppose you have a method on a type that is part of a transaction, and hence is bound by the rules specific to the context in which it was created. That type should inherit from ContextBoundObject so that the object is accessed from its own context, and the system can enforce the rules regarding the transactions associated with that object and its methods. If a ContextBoundObject is called from another context within the same application domain, a proxy is created for the caller, but the intercontext communication does not go through the channel system, which increases call efficiency in this situation.

Because crossing each boundary takes processing time, you should determine which boundaries your object must cross before deciding which type of remotable object your server should be. Objects specific to a particular context can be accessed directly only from that context. The same is true for objects specific to a particular application domain. To remote either object, the remoting system must successfully cross a context boundary, an application boundary, or both, before invoking the server object from within whatever boundary it is specific to. If you do not need a context check to call your object, you should not make your remote type extend ContextBoundObject; MarshalByRefObject will perform better. If you do need context checking, you should extend ContextBoundObject, but you need to understand that the additional boundary must be crossed before the call is made on your object.

See Also

Remotable and Nonremotable Objects | ContextBoundObject | Nonremotable Objects