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

Summary

In this chapter, we discussed a number of challenges that a remoting technology must meet. These challenges include critical issues, such as performance, interoperability, and security, as well as “nice to haves,” such as ease of configuration. .NET Remoting deals with these issues by offering the following features:

  • Strong, out-of-the-box support for common remoting scenarios such as high performance or strong interoperability

  • The ability to use the strong security features of IIS

  • Pluggable architecture for swapping in custom subsystems in the future

  • A logical object model for extending and customizing nearly every aspect of the remoting application.

Chapter 2

Understanding the .NET Remoting Architecture

In Chapter 1, “Understanding Distributed Application Development,” we took a tour of the distributed application development universe, noting various architectures, benefits, and challenges. This chapter will focus on the .NET Remoting architecture, introducing you to the various entities and concepts that you’ll use when developing distributed applications with .NET Remoting. A thorough understanding of the concepts discussed in this chapter is critical to understanding the rest of this book. Throughout the chapter, we’ll include some brief code snippets to give you a taste of the programmatic elements defined by the .NET Remoting infrastructure, but we’ll defer discussing full-blown implementation details until Chapter 3, “Building Distributed Applications with .NET Remoting.” If you’re already familiar with the .NET Remoting architecture, feel free to skim through this chapter and skip ahead to Chapter 3.

Remoting Boundaries

In the unmanaged world, the Microsoft Windows operating system segregates applications into separate processes. In essence, the process forms a boundary around application code and data. All data and memory addresses are process relative, and code executing in one process can’t access memory in another process without using some sort of interprocess communication (IPC) mechanism. One benefit of this address isolation is a more fault-tolerant environment because a fault occurring in one process doesn’t affect other processes. Address isolation also prevents code in one process from directly manipulating data in another process.

Because the common language runtime verifies managed code as type-safe and verifies that the managed code does not access invalid memory locations, the runtime can run multiple applications within a single process and still provide the same isolation benefits as the unmanaged application-per-process model. The common language runtime defines two logical subdivisions for .NET applications: the application domain and the context.

Application Domains

You can think of the application domain as a logical process. We say this because it’s possible for a single Win32 process to contain more than one application domain. Code and objects executing in one application domain can’t directly access code and objects executing in another application domain. This provides a level of protection because a fault occurring in one application domain won’t affect other application domains within the process. The division between application domains forms a .NET Remoting boundary.

Contexts

The common language runtime further subdivides an application domain into contexts. A context guarantees that a common set of constraints and usage semantics will govern all access to the objects within it. For example, a synchronization context might allow only one thread to execute within the context at a time. This means objects within the synchronization context don’t have to provide extra synchronization code to handle concurrency issues. Every application domain contains at least one context, known as the default context. Unless an object explicitly requires a specialized context, the runtime will create that object in the default context. We’ll discuss the mechanics of contexts in detail in Chapter 6, “Message Sinks and Contexts.” For now, realize that, as with application domains, the division between contexts forms a .NET Remoting boundary.

Crossing the Boundaries

.NET Remoting enables objects executing within the logical subdivisions of application domains and contexts to interact with one another across .NET Remoting boundaries. A .NET Remoting boundary acts like a semipermeable membrane: in some cases, it allows an instance of a type to pass through unchanged; in other cases, the membrane allows an object instance outside the application domain or context to interact with the contained instance only through a well-defined protocol—or not at all.

The .NET Remoting infrastructure splits objects into two categories: nonremotable and remotable. A type is remotable if—and only if—at least one of the following conditions holds true:

  • Instances of the type can cross .NET Remoting boundaries.

  • Other objects can access instances of the type across .NET Remoting boundaries.

Conversely, if a type doesn’t exhibit either of these qualities, that type is nonremotable.

Nonremotable Types

Not every type is remotable. Instances of a nonremotable type can’t cross a .NET Remoting boundary, period. If you attempt to pass an instance of a nonremotable type to another application domain or context, the .NET Remoting infrastructure will throw an exception. Furthermore, object instances residing outside an application domain or a context containing an object instance of a nonremotable type can’t directly access that instance.

Remotable Types

Depending on its category, a remotable type can pass through .NET Remoting boundaries or be accessed over .NET Remoting boundaries. .NET Remoting defines three categories of remotable types: marshal-by-value, marshal-by-reference, and context-bound.

Marshal-by-Value

Instances of marshal-by-value types can cross .NET Remoting boundaries through a process known as serialization. Serialization is the act of encoding the present state of an object into a sequence of bits. Once the object has been serialized, the .NET Remoting infrastructure transfers the sequence of bits across .NET Remoting boundaries into another application domain or context where the infrastructure then deserializes the sequence of bits into an instance of the type containing an exact copy of the state. In .NET, a type is serializable if it is declared by using the Serializable attribute. The following code snippet declares a class that’s made serializable by using the Serializable attribute:

[Serializable]
class SomeSerializableClass
{
     
}

In addition, a Serializable-attributed type can implement the ISerializable interface to perform custom serialization. We’ll discuss serialization in detail in Chapter 8. Figure 2-1 shows the serialization and deserialization of an object instance from one application domain to another application domain.

figure 2-1 marshal-by-value: object instance serialized from one application domain to another

Figure 2-1. Marshal-by-value: object instance serialized from one application domain to another

Marshal-by-Reference

Marshal-by-value is fine for some circumstances, but sometimes you want to create an instance of a type in an application domain and know that all access to such an object will occur on the object instance in that application domain rather than on a copy of it in another application domain. For example, an object instance might require resources that are available only to object instances executing on a specific machine. In this case, we refer to such types as marshal-by-reference, because the .NET Remoting infrastructure marshals a reference to the object instance rather than serializing a copy of the object instance. To define a marshal-by-reference type, the .NET Framework requires that you derive from System.MarshalByRefObject. Simply deriving from this class enables instances of the type to be remotely accessible. The following code snippet shows an example of a marshal-by-reference type:

class SomeMBRType : MarshalByRefObject 
{
     
}

Figure 2-2 shows how a marshal-by-reference remote object instance remains in its “home” application domain and interacts with object instances outside the home application domain through the .NET Remoting infrastructure.

figure 2-2 marshal-by-reference: object instance remains in its home application domain

Figure 2-2. Marshal-by-reference: object instance remains in its home application domain

Context-Bound

A further refinement of marshal-by-reference is the context-bound type. Deriving a type from System.ContextBoundObject will restrict instances of such a type to remaining within a specific context. Objects external to the containing context can’t directly access ContextBoundObject types, even if the other objects are within the same application domain. We’ll discuss context-bound types in detail in Chapter 6, “Message Sinks and Contexts.” The following code snippet declares a context-bound type:

class SomeContextBoundType : ContextBoundObject
{
     
}

Figure 2-3 shows the interactions between a Context-Bound object and other objects outside its context.

figure 2-3 context-bound: remote objects bound to a context interact with objects outside the context through the .net remoting infrastructure

Figure 2-3. Context-bound: remote objects bound to a context interact with objects outside the context through the .NET Remoting infrastructure