MTS simplifies the task of developing application components by allowing you to perform work with transactions. This protects applications from anomalies caused by concurrent updates or system failures.
Transactions maintain the ACID properties:
The intermediate states of a transaction are not visible outside the transaction, and either all the work happens or none of it does. This allows you to develop application components as if each transaction executes sequentially and without regard to concurrency. This is a tremendous simplification for application developers.
You can declare that a component is transactional, in which case MTS associates transactions with the component's objects. When an object's method is executed, the services that resource managers and resource dispensers perform on its behalf execute under a transaction. This can also include work that it performs for other MTS objects. Work from multiple objects can be composed into a single atomic transaction.
Multiple objects composed into a transaction
Without transactions, error recovery is extremely difficult, especially when multiple objects update multiple databases. The possible combinations of failure modes are too great even to consider. Transactions simplify error recovery. Resource managers automatically undo the transaction's work, and the application retries the entire business transaction.
Transactions also provide a simple concurrency model. Because a transaction's isolation prevents one client's work from interfering with other clients, you can develop components as though only a single client executes at a time.
Components Declare Transactional Requirements
Every MTS component has a transaction attribute that is recorded in the MTS catalog. MTS uses this attribute during object creation to determine whether the object should be created to execute within a transaction, and whether a transaction is required or optional. For more information on transaction attributes, see Transaction Attributes.
Components that make updates to multiple transactional resources, such as database records, for example, can ensure that their objects are always created within a transaction. If the object is created from a context that has a transaction, the new context inherits that transaction; otherwise, the system automatically initiates a transaction.
Components that only perform a single transactional update can be declared to support, but not require, transactions. If the object is created from a context that has a transaction, the new context inherits that transaction. This allows the work of multiple objects to be composed into a single atomic transaction. If the object is created from a context that does not have a transaction, the object can rely on the resource manager to ensure that the single update is atomic.
How Work Is Associated with a Transaction
An object's associated context object indicates whether the object is executing within a transaction and, if so, the identity of the transaction.
Resource dispensers can use the context object to provide transaction-based services to the MTS object. For example, when an object executing within a transaction allocates a database connection by using the ODBC resource dispenser, the connection is automatically enlisted on the transaction. All database updates using this connection become part of the transaction, and are either atomically committed or aborted. For more information, see Enlisting Resources in Transactions.
Stateful and Stateless Objects
Like any COM object, MTS objects can maintain internal state across multiple interactions with a client. Such an object is said to be stateful. MTS objects can also be stateless, which means the object does not hold any intermediate state while waiting for the next call from a client.
When a transaction is committed or aborted, all of the objects that are involved in the transaction are deactivated, causing them to lose any state they acquired during the course of the transaction. This helps ensure transaction isolation and database consistency; it also frees server resources for use in other transactions.
Completing a transaction enables MTS to deactivate an object and reclaim its resources, thus increasing the scalability of the application. Maintaining state on an object requires the object to remain activated, holding potentially valuable resources such as database connections. Stateless objects are more efficient and are thus recommended. For more information on object deactivation, see Deactivating Objects.
How Objects Can Participate in Transaction Outcome
You can use methods implemented on the IObjectContext interface to enable an MTS object to participate in determining a transaction's outcome. The SetComplete, SetAbort, DisableCommit, and EnableCommit methods work in conjunction with the component's transaction attribute to allow one or more objects to be composed simply and safely within transactions.
Both SetComplete and SetAbort deactivate the object on return from the method. MTS reactivates the object on the next call that requires object execution.
Objects that need to retain state across multiple calls from a client can protect themselves from having their work committed prematurely by the client. By calling DisableCommit before returning control to the client, the object can guarantee that its transaction cannot successfully be committed without the object doing its remaining work and calling EnableCommit.
Client-Controlled vs. Automatic Transactions
Transactions can either be controlled directly by the client, or automatically by the MTS run-time environment.
Clients can have direct control over transactions by using a transaction context object. The client uses the ITransactionContext interface to create MTS objects that execute within the client's transactions, and to commit or abort the transactions.
Transactions can automatically be initiated by the MTS run-time environment to satisfy the component's transaction expectations. MTS components can be declared so that their objects always execute within a transaction, regardless of how the objects are created. This feature simplifies component development, because you do not need to write application logic to handle the special case where an object is created by a client not using transactions.
This feature also reduces the burden on client applications. Clients do not need to initiate a transaction simply because the component that they are using requires it.
MTS automatically initiates transactions as needed to satisfy a component's requirements. This event occurs, for example, when a client that is not using transactions creates an object in an MTS component that is declared to require transactions.
MTS completes automatic transactions when the MTS object that triggered their creation has completed its work. This event occurs when returning from a method call on the object after it has called SetComplete or SetAbort. SetComplete causes the transaction to be committed; SetAbort causes it to be aborted.
A transaction cannot be committed while any method is executing in an object that is participating in the transaction. The system behaves as if the object disables the commit for the duration of each method call.
Building Transactional Components, Multiple Transactions