Overview of COM+
This section will provide you with an overview of some of the services and features that COM+ provides for serviced components.
COM+ began its life as MTS (Microsoft Transaction Services). MTS was a system designed to facilitate distributed transactions across multiple software components. The advent of component-centric programming showed a need for the ability to extend the concept of a transaction outside a single database. Transactions needed to be able to deal with multiple components that performed data operations on multiple, possibly distant, data sources.
After the distributed transaction system was in place, other features began falling into place as well, turning MTS from a simple transaction coordinator into a full-fledged enterprise application hosting environment. MTS eventually grew into what is now COM+. COM+ can be managed through the Component Services section of your operating system's Administrative Tools section.
In COM+, you create an application, which is a container for COM+ components. Applications can exist in two different modes: Server mode and Library mode. An application in Server mode is hosted on the COM+ server by the COM+ process itself. When clients attach to a component in a Server mode application, they attach via Remoting and the components remain in the server host process's context. On the other hand, a Library mode application has the components instantiated within the thread context of the calling client. The difference between Server mode and Library mode applications is where the code is executed. COM+ Server mode applications execute all the code on the server in which they are configured, whereas COM+ Library mode applications execute on the client.
A transaction can be thought of as a single, atomic unit of work. In a database, you begin a transaction and then perform several operations on multiple tables. If something goes wrong during one of those operations, you need to be able to restore the database to its state at the moment before the transaction began. If nothing goes wrong, you need to be able to commit the changes made during the transaction to make them permanent.
The same needs apply to a COM+ transaction. The only difference is that a COM+ transaction is a distributed transaction that can occur between multiple components. In a typical scenario, a component begins a transaction, creating a transaction root. Every method call on every component below that root can then be enlisted in the transaction, based on the configuration of the child components. If the component is involved in the transaction, it can then vote for whether the transaction is committed or is rolled back.
At the end of the execution of the method that started the transaction, if every involved component votes to commit the transaction, the transaction will be committed. If even one component votes against committing the transaction due to some application failure, the entire transaction will be rolled back. Every action performed by every involved component since the beginning of the transaction will be rolled back.
People often take it for granted that actions performed by their components will be rolled back. However, if the data source you are working with doesn't support the MS DTC transaction process, you will have to roll back those changes yourself. SQL Server, Access, and OLE DB can all be rolled back. But if you did something such as call a web service during a COM+ transaction, you'll have to call the web service again to undo the changes made when the transaction was rolled back.
JIT Activation in COM+
JIT is an acronym that stands for just-in-time activation. When a COM+ application is configured in Server mode with object pooling enabled (discussed shortly), the application can take advantage of JIT activation.
JIT is an advanced feature that allows for pooled objects to mitigate the sometimes high cost of instantiation. Certain applications require that when some components are first instantiated, they obtain resources and perform certain tasks that take a considerable amount of time. In a large, multiuser application in which objects are instantiated and disposed of constantly, that large initial overhead can bring an application to its knees and drastically reduce performance.
JIT allows for objects to be deactivated when not in use, even if clients are holding open references to them. This means that the resources the objects consume are temporarily given back to the system until they are needed again. When a deactivated reference to a JIT-managed object is invoked, the object is reactivated. These processes are called deactivation and activation. You can think of these as pseudo constructors and destructors. As a programmer, you can write code that will be executed upon activation and deactivation. If you know in advance that your application can take advantage of JIT, you can write your components to make the best use of the JIT system and activation.
As mentioned in the previous section, components often have a high instantiation cost and high-volume systems that are constantly instantiating new objects can create a performance problem that can cripple an application.
Object pooling is one answer to this problem. When an object is first requested, it is instantiated and the full cost of instantiating the object is incurred. However, this is where the similarity between standard instantiation and pooling/JIT disappears. After it has been instantiated, the object is considered pooled. When the object becomes inactive for a set period of time, it will be returned to the pool. If the object is requested again, it will be fetched from a pool instead of instantiated from scratch. This defers a lot of the performance cost of instantiation and allows an application to handle higher volumes much more smoothly.
As a COM+ programmer, you can give your object event handlers that will handle the events of being placed into a pool (deactivated) or being retrieved from the pool for use by a client object (activated). As mentioned in the JIT section, you can perform the heavy lifting in the instantiation (construction) phase, and perform only minimal maintenance code in the activation and deactivation handlers. This will allow your COM+ application to function extremely well in a high volume environment that is constantly requesting new objects. Another thing you can do as the developer or administrator is to configure the size of the object pool for optimal performance.
COM+ components (classes that derive from ServicedComponent) are instantiated by the COM+ system. Objects can also be constructed. This allows for an initial state to be given to the component by an administrator. Within the COM+ administration console, you can set the construction string for every component in the application. That string is then sent to the component by means of the Construct method. Every time a component is then constructed by the infrastructure, its Construct method is invoked, allowing variable initialization parameters to be passed as startup options.
Role-based security provides an easy-to-use system for administering users, groups, and permissions. The idea is that instead of giving specific privileges to specific users, privileges are granted to roles or groups. Users are then associated with those groups; the privileges of the users are obtained by looking at the privileges granted to the groups to which the user belongs.
In the case of COM+ Enterprise Services, security can be enabled at the protocol level. That means if a COM+ component is properly configured, it can be made aware of the security context under which it is running for any and all methods invoked. For example, if a user named johndoe in the domain MYDOMAIN uses an application that employs a COM+ application, the COM+ components within that application can be allowed to determine the identity of the user at runtime.
Not only can the components determine the identity of the user at runtime, they can determine the groups to which that user belongs. A special class called ContextUtil, which is made available to all ServicedComponent classes, can be used to detect whether security is enabled. If so, ContextUtil can determine whether the current user belongs to a given group with the method IsCallerInRole. If you have had experience with .NET security, you'll notice that this method looks a lot like the IsInRole method that is part of the IPrincipal interface. You will see how to write this security code later in the chapter in the "Building COM+ Components" section.
A queued component is actually a collection of several interconnected pieces of technology. Queued components provide an asynchronous method by which COM+ components can have their methods invoked. If the processing of a method is complicated and could take a long time, but the client code doesn't need to know the result right away, queued components can dramatically increase performance.
A queued component works by wiring up a Microsoft Message Queuing (MSMQ) queue to a COM+ component. Every time a method call is made to the component, the parameters for the invocation are serialized and stored in a message in the queue. At some later point (the actual time is determined by the CPU and resource load on the machine at the time), the queue is examined. Each message in the queue is deserialized and rebuilt into a method invocation. That method is then called on the COM+ component.
Because the method invocations are done out-of-sync with the client code, queued components cannot have any nonvoid methods. In other words, if you want to be able to queue method calls to a component, those methods calls need to take serializable parameters and cannot return any data.
As you will see later in the chapter, there is a special syntax to instantiating components that are associated with queues.
Events are based on a publisher/subscriber model. When some event of importance takes place within a component, form, control, or class, that class can then fire the event. By firing an event, the class is actually notifying all the event subscribers that the important event took place, and it is providing additional information regarding the event. This model is referred to as tightly coupled events.
The event model supported and provided by COM+ is referred to as loosely coupled events. A loosely coupled event is actually a COM+ component. When an important event takes place in a COM+ application, the application can then publish that event. All subscribers of the event, which are also COM+ components, are then notified. It is considered loosely coupled because an event from one application can be subscribed to by another application and the applications will work. Subscriptions can be managed programmatically or through the COM+ administrative tools. Creating an event capable of participating in the COM+ event system requires the use of a custom attribute that you will see later.