MTS Programming Concepts

Archived content. No warranty is made as to technical accuracy. Content may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist.

This topic explains the key concepts that a component application developer needs to understand to develop Microsoft Transaction Server (MTS) applications.

On This Page

Transactions
Transaction Attributes
Enlisting Resources in Transactions
Determining Transaction Outcome
MTS Objects
Context Objects
Creating MTS Objects
Transaction Context Objects
Passing Parameters
Passing Object References
Deactivating Objects
Object Pooling and Recycling
MTS Clients
Base Clients
Base Clients vs. MTS Components
Activities
Components and Threading
Programmatic Security
Basic Security Methods
Advanced Security Methods
Error Handling

Transactions

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:

  • Atomicity ensures that all the updates completed under a specific transaction are committed and made durable, or that they get aborted and rolled back to their previous state.

  • Consistency means that a transaction is a correct transformation of the system state, preserving the state invariants.

  • Isolation protects concurrent transactions from seeing each other's partial and uncommitted results, which might create inconsistencies in the application state. Resource managers use transaction-based synchronization protocols to isolate the uncommitted work of active transactions.

  • Durability means that committed updates to managed resources, such as a database record, survive failures, including communication failures, process failures, and server system failures. Transactional logging even allows you to recover the durable state after disk media failures.

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.

Cc723271.vi0402(en-us,TechNet.10).gif

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.

  • SetComplete indicates that the object has successfully completed its work for the transaction. The object is deactivated upon return from the method that first entered the context.

  • SetAbort indicates that the object's work can never be committed. The object is deactivated upon return from the method that first entered the context.

  • EnableCommit indicates that the object's work is not necessarily done, but that its transactional updates can be committed in their current form.

  • DisableCommit indicates that the object's transactional updates can not be committed in their current form.

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.

Transaction Attributes

Every MTS component has a transaction attribute. The transaction attribute can have one of the following values:

  • Requires a transaction. This value indicates that the component's objects must execute within the scope of a transaction. When a new object is created, its object context inherits the transaction from the context of the client. If the client does not have a transaction, MTS automatically creates a new transaction for the object.

  • Requires a new transaction. This value indicates that the component's objects must execute within their own transactions. When a new object is created, MTS automatically creates a new transaction for the object, regardless of whether its client has a transaction.

  • Supports transactions. This value indicates that the component's objects can execute within the scope of their client's transactions. When a new object is created, its object context inherits the transaction from the context of the client. If the client does not have a transaction, the new context is also created without one.

  • Does not support transactions. This value indicates that the component's objects do not run within the scope of transactions. When a new object is created, its object context is created without a transaction, regardless of whether the client has a transaction.

Most MTS components are declared as either Supports transactions or Requires a transaction. These values allow an object to execute within the scope of its client's transaction. You can see the difference between these values when an object is created from a context that does not have a transaction. If the component's transaction attribute is Supports transactions, the new object runs without a transaction. If it is declared as Requires a transaction, MTS automatically initiates a transaction for the new object.

Declaring a component as Requires a new transaction is similar to using Requires a transaction in that the component's objects are guaranteed to execute within transactions. However, when you declare the transaction attribute this way, an object never runs inside the scope of its client's transaction. Instead, the system always creates independent transactions for the new objects. For example, you can use this for auditing components that record work done on behalf of another transaction regardless of whether the original transaction commits or aborts.

Specifying Does not support transactions ensures that an object's context does not contain a transaction. This value is the default setting and is primarily used with versions of COM components that precede MTS.

Setting the Transaction Attribute

The transaction attribute is part of a component definition. The component developer determines it, and changes to it are not recommended.

You can set a transaction attribute at development time by:

  • Using values defined in Mtxattr.h. You can specify these values in an .odl file to encode them into the component's type library.

  • Using the MTS Explorer to create a package file for deploying your components.

Because Because Microsoft® Visual Basic® automatically generates a type library, Visual Basic developers must use the MTS Explorer to set the transaction attribute.

Enlisting Resources in Transactions

MTS provides automatic transactions, which means that transaction requirements are declared as component properties. MTS automatically begins transactions and commits or aborts these transactions on behalf of the component, based on the component's transaction property.

Automatic transactions work because resource dispensers pass transactions to the resource manager. For example, the ODBC Driver Manager is a resource dispenser for ODBC database connections. When a database connection is requested from a transactional component, the ODBC Driver Manager obtains the transaction from the object's context. The ODBC Driver Manager then associates (enlists) the database connection with the transaction.

If you do not have a resource dispenser, you can build your own using the Microsoft Transaction ServerSoftware Development Kit (SDK), which is located in the Microsoft Platform SDK.. For more information, see the Resource Dispenser Guide and the samples included with the MTS SDK.

For a resource manager to participate in an MTS transaction, it must support one of the following protocols:

  • OLE Transactions

  • The X/Open DTP XA standard

OLE Transactions, the object-oriented, two-phase commit protocol defined by Microsoft, is the preferred protocol. OLE Transactions is based on the Component Object Model (COM) and is used by resource managers in order to participate in distributed transactions coordinated by Microsoft Distributed Transaction Coordinator (DTC). OLE Transactions is supported by Microsoft SQL Server version 6.5. For more information on supporting OLE Transactions, see the Resource Manager Guide and the samples included with the MTS SDK.

XA is the two-phase commit protocol defined by the X/Open DTP group. XA is natively supported by many Unix databases, including Informix, Oracle, and DB2.

For MTS to work with XA-compliant resource managers, an OLE Transactions-to-XA mapper, provided by the MTS SDK, makes it relatively straightforward for XA-compliant resource managers to provide resource dispensers that can accept OLE Transactions from MTS and translate to XA. For more information, see "Mapping OLE Transactions to the XA Protocol" in the MTS SDK, or contact your resource manager vendor.

Determining Transaction Outcome

This section discusses how applications determine whether a transaction will commit or abort.

First, it is important to understand that the MTS objects involved in a transaction do not need to know the transaction outcome. All objects involved in a transaction are automatically deactivated. Deactivation causes the objects to lose any state that they acquired during the transaction. Consequently, their behavior is not affected by the outcome of the transaction.

Each object can participate in determining the outcome of a transaction. Objects call SetComplete, SetAbort, EnableCommit, and DisableCommit, based on the desired component behavior. For example, an object would typically call SetAbort after receiving an error from a database operation, on a method call to another object, or due to a violation of a business rule such as an overdrawn account.

The client of the transaction determines its success or failure (commit or abort) based on values returned from the method call that caused the transaction to complete. The client can be either a base client or another MTS object that exists outside the transaction. The client must know which methods cause transactions to complete and how the method output values can be used to determine success (commit) or failure (abort).

An object method that intends to commit a transaction typically returns an HRESULT value of S_OK after calling SetComplete. On return, MTS automatically completes the transaction. If the transaction commits, the S_OK value is returned to the client. If it aborts, the HRESULT value is changed to CONTEXT_E_ABORTED. The client can use these two values to determine the outcome.

An object method typically notifies its client that it has forced the transaction to abort by calling SetAbort in one of two ways:

  • Return S_OK and use an output parameter to indicate the failure.

  • Return an HRESULT error code. Different codes could be used to distinguish different causes, or the generic CONTEXT_E_ABORTED error could be used.

For example, the Sample Bank application uses an output parameter to indicate failure:

Public Function Perform(lngPrimeAccount As Long, _
   lngSecondAccount As Long, lngAmount As Long, _
   strTranType As String, ByRef strResult As String) _
   As Long
' get our object context
Dim ctxObject As ObjectContext
Set ctxObject = GetObjectContext()
On Error GoTo ErrorHandler
' check for security
If (lngAmount > 500 Or lngAmount < -500) Then
    If Not ctxObject.IsCallerInRole("Managers") Then
        Err.Raise Number:=ERROR_NUMBER, _
        Description:="Need 'Managers' role " + _
           "for amounts over $500"
    End If
End If
.
.
.
ctxObject.SetComplete       ' we are finished and happy
Perform = 0
Exit Function
ErrorHandler:
ctxObject.SetAbort           ' we are unhappy
strResult = Err.Description  ' return the error message
Perform = -1           ' indicate that an error occured
End Function

It is also important to note that there are failure scenarios where the client cannot determine the transaction outcome. This situation results, for example, when a call failure occurs due to a transport error such as RPC_E_CONNECTION_TERMINATED). In such cases, it is necessary to use an application-defined protocol to determine the transaction outcome.

On clustered servers, MTS will not automatically reconnect to MS DTC in the event of a failover. Not enough information exists about the transaction composition and state to determine the appropriate course of action. Retries remain the responsibility of the client application. The client cannot differentiate an error caused by failover from other errors.

Resource managers are guaranteed to get transaction outcomes as part of the two-phase commit protocol managed by the Microsoft Distributed Transaction Coordinator. This feature allows resource managers to manage locks and to determine whether it is necessary to make state changes permanent or to discard them.

MTS Objects

An MTS object is an instance of an MTS component. MTS maintains context for each object. This context, which is implicitly associated with the object, contains information about the object's execution environment, such as the identity of the object's creator and, optionally, the transaction encompassing the work of the object. The object context is similar in concept to the process context that an operating system maintains for an executing program.

vi0403

An MTS object and its associated context object

An MTS object and its associated context object have corresponding lifetimes. MTS creates the context before it creates the MTS object. MTS destroys the context after it destroys the MTS object.

Context Objects

Each MTS object has an associated context object. A context object is an extensible MTS object that provides context for the execution of an instance, including transaction, activity, and security properties. When an MTS object is created, MTS automatically creates a context object for it. When the MTS object is released, MTS automatically releases the context object.

vi0403

An MTS object and its associated context object

An MTS object's context has intrinsic properties that are determined during object creation. These properties include the identity of the client that initiated the object's creation and whether or not the object executes within the scope of a transaction.

The properties established for the new object context are determined by a combination of:

  • The transaction attributes of the component as specified in the MTS catalog.

  • The properties of the context from which the new object is created. For example, the client's context may contain a transaction.

If your application uses Microsoft Internet Information Server (IIS), you can retrieve IIS intrinsic objects as follows:

  • Using Visual Basic, by calling the Item method of the context object.

  • Using Microsoft Visual C++® or Microsoft Visual J++™, by calling the GetProperty method of the IGetContextProperties interface.

For more information on IIS intrinsic objects, see the IIS documentation.

Contexts Are Implicit

MTS maintains an implicit relationship between an MTS object and its context. This feature eliminates the need for you to pass explicitly a context object through your application.

You can access an MTS object's context by calling the GetObjectContext function. This function returns a reference to the IObjectContext interface. Resource dispensers and other context-aware services can also access the object's context. This permits the ODBC resource dispenser automatically to enlist connections on the object's transaction.

Before a method of an MTS object is dispatched for execution, that object's context becomes the current context for the thread. This context remains current as long as the object remains within the context. Calling a method in a different context causes that context to become current; the caller's context is automatically restored on return from the method.

Managing References to the Context Object

You must not pass a reference to the context object outside the object. You must explicitly release every reference that you acquire on the object context.

The context object is not available during calls to the component's class factory. This means, for example, that a Visual C++ class implementation cannot access a context object during calls to a constructor or destructor. Objects that require access to the context object during initialization or destruction should implement IObjectControl. For more information, see Deactivating Objects.

Creating MTS Objects

You can create MTS objects by:

  • Using context objects.

  • Using transaction context objects.

  • Using standard COM functions like CoCreateInstance or CreateObject.

Note: If you are using Visual C++ and running an MTS component in-process, you must:

  • Call CoInitialize(NULL) before requesting services from MTS or creating an MTS object.

  • Call CoInitializeSecurity to initialize process-specific security. MTS security is disabled when loading an MTS object in-process.

  • Call CoUninitialize only after you have finished using MTS or MTS objects, preferably just prior to terminating your application. You cannot call CoInitialize again and invoke more MTS services. Once CoUninitialize has been called, your application no longer executes in the MTS run-time environment.

Creating Objects Using a Context Object

You can create an MTS object by calling the CreateInstance method on the IObjectContext interface of an object's context object. The new MTS object's context inherits the activity, possibly a transaction, and all security identities from the creating object's context.

vi0405

Creating an object using a context object

Do Not Create Non-MTS Local Servers Using CreateInstance

Creating non-MTS local servers using the context object's CreateInstance method is strongly discouraged. Such servers will not scale well and may result in additional server instances that are not shut down by MTS. If you need to create a non-MTS local server, use CoCreateInstance.

Do not use COM surrogates within MTS. Instead, load in-process servers into MTS server processes.

Creating Objects Using a Transaction Context Object

If you want your base client to control transaction boundaries, use a transaction context object*.* You can create an MTS object by calling the CreateInstance method of the ITransactionContext interface. The new MTS object's context inherits the activity, possibly a transaction, and the identity of the initial client from the transaction context object. You can call the Commit method to commit an object's work and the Abort method to abort its work.

vi0404

Creating an object using a transaction context object

Creating Objects Using CoCreateInstance

You can create MTS objects by using CoCreateInstance or any equivalent method based on CoGetClassObject and IClassFactory::CreateInstance. While this approach should suffice for many base client applications, there are some significant limitations for the client, including the inability to control transaction boundaries. Base clients that need this additional level of control can use a transaction context object.

vi0406

Instantiating an object with CoCreateInstance

When you use CoGetClassObject with a component that is registered to run under MTS, it returns a reference to an MTS-provided class factory. This allows MTS to participate in the client's calls to IClassFactory::CreateInstance. The MTS class factory creates the context object and then calls the component's real class factory.

For clustered servers, if you are using the CoCreateInstanceEx function, use the name of the virtual server containing the MSDTC resource in the pwszName field of the COSERVERINFO structure. (See the Microsoft Platform SDK documentation for more details about CoCreateInstanceEx.)

Important: It is recommended that you do not call CoCreateInstance to create MTS objects from within MTS objects. When you do so, the new object's context cannot inherit any properties from its client's context. In particular, the new object cannot execute within the scope of its client's transaction.

Aggregation

You cannot use an MTS object as part of an aggregate of other objects. CoCreateInstance returns CLASS_E_NOAGGREGATION to indicate an attempt to create an MTS object with another controlling IUnknown.

You can, however, create an MTS object that is implemented as an aggregation of objects.

Creating Objects Using Visual Basic

You can use the following object creation methods in Microsoft® Visual Basic® to create MTS objects:

  • The CreateObject function

  • The GetObject function

  • The New keyword (see the Important: note for limitations)

  • Automatically if the object is an Application object

Using Visual Basic object creation methods results in the same limitations as using CoCreateInstance. To inherit a transaction from the creating object's context, use CreateInstance.

Important: Do not use the New operator, or a variable declared As New, to create an instance of a class that is part of the active project. In this situation, Visual Basic uses an implementation of object creation that does not use COM. To prevent this occurrence, it is recommended that you mark all objects passed out from a Visual Basic componentas Public Creatable, or its equivalent, and created with either the CreateObject function or the CreateInstance method of the ObjectContext object.

Transaction Context Objects

The transaction context object allows base clients to combine the work of multiple MTS objects into a single transaction, without having to develop a new component specifically for that purpose.

The transaction context object's methods use its context object as follows:

  • CreateInstance—Calls CreateInstance and returns a reference to the newly created object.

    Cc723271.context1(en-us,TechNet.10).gif

  • Commit—Calls SetComplete and returns.

    Cc723271.context2(en-us,TechNet.10).gif

  • Abort—Calls SetAbort and returns.

    Cc723271.context3(en-us,TechNet.10).gif

The transaction context component is defined as Requires a New Transaction. You cannot use the transaction context object to enlist in an existing transaction.

For example, suppose you have two components, Walk and ChewGum. Each component is defined as Supports Transactions and calls SetComplete when it is finished with its work. A base client could compose the work done by each component in a single transaction.

Dim objTxCtx As TransactionContext
Dim objWalk As MyApp.Walk
Dim objChewGum As MyApp.ChewGum
' Get TransactionContext
Set objTxCtx = _
     CreateObject("TxCtx.TransactionContext")
' Create instances of Walk and ChewGum
Set objWalk = _
     objTxCtx.CreateInstance("MyApp.Walk")
Set objChewGum = _
     objTxCtx.CreateInstance("MyApp.ChewGum")
' Both components do work
objWalk.Walk
objChewGum.ChewGum
' Commit the transaction
objTxCtx.Commit

Transaction Context Object Limitations

Note the following limitations when using a transaction context object:

  • Transaction composition

  • Location transparency

  • Base client does not have context

Transaction Composition

When using a transaction context object, the application logic that composes the work into a single transaction is tied to a specific base client implementation and the advantages of using MTS components are lost. These implementations include:

  • Ability to reuse the application logic as part of an even larger transaction

  • Imposition of declarative security

  • Flexibility to run the logic remotely from the client

Location Transparency

The transaction context object runs in-process with the base client, which means that MTS must exist on the base client computer. This may not be a problem, for example when the transaction context object is used from an Active Server Page (ASP) that is running on the same server as MTS.

Base Client Does Not Have Context

You do not get a context for the base client when you create a transaction context object. Transactional work can only be done indirectly, through MTS objects created by using the transaction context object. In particular, the base client cannot use MTS resource dispensers (such as ODBC) and have the work included in of the transaction. For example, developers may be familiar with the following syntax for doing transactional work on relational database systems:

BEGIN TRANSACTION
     DoWork
COMMIT TRANSACTION

Using the transaction context object in a similar way does not yield the desired result:

Set objTxCtx = CreateObject("TxCtx.TransactionContext")
     DoWork
     objTxCtx.Commit
Set objTxCtx = Nothing

The call to DoWork in this example will not be enlisted in a transaction. You must build an MTS component that calls DoWork, create an object instance of that component using the transaction context object, then call that object from the base client in order for the work to be part of the client-controlled transaction.

Passing Parameters

This topic covers the following:

  • Parameter types and marshaling

  • Objects as parameters

  • Passing large data

Parameter Types and Marshaling

MTS object interfaces must be able to be marshaled. Marshaling interfaces allows calls across thread, process, and machine boundaries.

Standard COM marshaling is used. This means MTS object interfaces must either:

  • Have method parameters which are Automation data types and be described in a type library, or

  • Use custom interfaces with a MIDL-generated proxy-stub DLL.

For more information on type libraries and proxy-stub DLLs, see MTS Component Requirements.

Custom marshaling is not used. Even if a component supports the IMarshal interface, its IMarshal methods are never called by the MTS run-time environment.

VBScript Parameters

Components that are intended for use from Active Server Pages (ASPs) using Microsoft Visual Basic® Scripting Edition (VBScript) should support IDispatch and limit method parameter types as follows:

  • **VBScript version 1.0—**Any Automation type may be passed by value, but not by reference. Method return values must be of type Variant.

  • **VBScript version 2.0—**Same as VBScript version 1.0, except parameters of type Variant may now be passed by reference.

Objects as Parameters

Whether an object is passed by value or by reference is not specified by the client, but is a characteristic of the object itself. Basic COM objects can either be passed by reference or by value, depending on their implementation. If the COM object uses standard marshaling, then it is passed by reference. COM objects can also implement IMarshal to copy data by value. MTS objects are always passed by reference.

Additionally, the function of the object affects how it should be passed as a parameter. When deciding whether to pass objects by value or by reference, it is useful to classify the objects as follows:

  • **Recordset Objects—**Encapsulate raw data, such an ADO recordset. Recordset objects are not registered as MTS objects.

  • **Business Object—**Encapsulate business logic; for example, an order-processing component. Business objects should be registered as MTS objects.

The following table describes when to pass recordset objects by value or by reference:

Pass Parameter

If

Client Requirements

 

 

 

By value

Data is relatively small

Recipient requires all data and can get data without reaccessing caller.

By reference

Data is relatively large

Recipient does not require all data and must reaccess caller, possibly many times.

Note: Whether data is "small" or "large" also depends on the speed of the connection. For example, if the component is accessed over a corporate intranet, a much larger recordset can be passed to the client in one call than in a call made by a client accessing the component on an Internet server over a modem.

Because business objects are MTS objects, they are always passed by reference.

Passing Large Data

When returning a large amount of data, consider using a Microsoft Active Data Objects (ADO) Recordset object. In particular, the Microsoft Advanced Data Connector (ADC) provides a recordset implementation that can be disconnected from the server and marshaled by value to the client.

Cc723271.param1(en-us,TechNet.10).gif

The disconnected recordset moves state to the client, allowing server resources to be freed. The client can make changes to the recordset and reconnect to the server to submit updates. For more information on state, see Holding State in Objects.

Another method of packaging large amounts of data is to use safe arrays. For example, when using Microsoft Remote Data Objects (RDO), you can use the rdoResultSet.GetRows method to copy rows into an array, and then pass the array back to the client. This requires fewer calls and is more efficient than issuing MoveNext calls across the network for each row.

Passing Object References

You must ensure that MTS object references are only exchanged in the following ways:

  • Through return from an object creation interface, such as CoCreateInstance (or its equivalent), ITransactionContext::CreateInstance, or IObjectContext::CreateInstance.

  • Through a call to QueryInterface.

  • Through a method that has called SafeRef to obtain the object reference.

An object reference that is obtained in these ways is called a safereference. MTS ensures that methods invoked using safe references execute within the correct context.

vi0407

Using SafeRef to pass a reference to an object

Note: It is not safe to exchange references by any other means. In particular, do not pass interfaces outside the object by using global variables. These restrictions are similar to those imposed by COM for references passed between apartments.

Calls that use safe references always pass through the MTS run-time environment. This allows MTS to manage context switches and allows MTS objects to have lifetimes that are independent of client references. For more information, see Deactivating Objects.

Callbacks

It is possible to make callbacks to clients and to other MTS components. For example, you can have an object that creates another object. The creating object can pass a reference to itself to the created object; the created object can then use this reference to call the creating object.

If you choose to use callbacks, note the following restrictions:

  • Calling back to the base client or another package requires Access-level security on the client. Additionally, the client must be a DCOM server.

  • Intervening firewalls may block calls back to the client.

  • Work done on the callback executes in the environment of the object being called. It may be part of the same transaction, a different transaction, or no transaction.

  • The creating object must call SafeRef and pass the returned reference to the created object in order to call back to itself.

Deactivating Objects

MTS extends COM to allow object deactivation, even while client references are maintained. This makes server applications more scalable by allowing server resources to be used more efficiently.

MTS objects are initially created in the deactivated state. When a client invokes a method on an object that is in a deactivated state, MTS automatically activates the object. During activation, the object is put into its initial state.

Note: For Visual C++ developers, calls to QueryInterface, AddRef, or Release do not cause activation.

This ability for an object to be deactivated and reactivated while clients hold references to it is called just-in-time activation. From the client's perspective, only a single instance of the object exists from the time the client creates it to the time it is finally released. In actuality, it is possible that the object has been deactivated and reactivated many times.

vi0408

Just-in-time activation

The context object exists for the entire lifetime of its MTS object, even across one or more deactivation and reactivation cycles.

Object deactivation allows clients to hold references for long periods of time with limited consumption of server resources. Consider, for example, a client application that spends 99 percent of its time between transactions. In this case, the MTS objects are activated less than 1 percent of the time.

When is an object deactivated?

An MTS object is deactivated when any of the following occurs:

  • The object requests deactivation.

    An object can request deactivation by using the IObjectContext interface. You can use the SetComplete method to indicate that the object has successfully completed its work and that the internal object state doesn't need to be retained for the next call from the client. Similarly, SetAbort indicates that the object cannot successfully complete its work and that its state does not need to be retained.

    You can develop stateless objects by using MTS objects that deactivate on return from every method.

  • A transaction is committed or aborted.

    MTS does not allow an object to maintain private state that it acquired during a transaction. When an object's transaction is committed or aborted, the object is deactivated. Of these deactivated objects, the only ones that can continue to exist are the ones that have references from clients outside the transaction. Subsequent calls to these objects reactivate them and cause them to execute in the next transaction.

  • The last client releases the object.

    This occurrence is listed here for completeness. The object is deactivated and never reactivated. The object's context is also released.

How are objects deactivated?

MTS deactivates an object by releasing all its references to the object. This causes properly developed components to destroy the object; this feature also requires the component to follow the MTS reference passing rules (see Passing Object References) and the COM reference counting rules.

Note: MTS writes an Informational message to the event log when objects that do not report their reference count are deactivated.

Application components are responsible for releasing object resources on deactivation. This includes:

  • Resources that are allocated with MTS resource dispensers, such as ODBC database connections.

  • All other resources, including references to other objects (including MTS objects and context objects) and memory held by any instances of the component, such as using delete this in C++).

Doing Additional Work on Activation and Deactivation

If an object is not already activated and it supports IObjectControl, MTS calls the Activate method prior to initiating the client request. Components can use the Activate method to initialize objects. This is especially important for initialization that requires access to the context object. Here, keep in mind that the context is not available during calls to the component's class factory. Having access to the context object through Activate allows you to pass a reference to the context object to other methods; this reference can then be released in the Deactivate method. The Activate method is also useful for objects that support pooling (see Object Pooling and Recycling).

For objects that support the IObjectControl interface, MTS calls the Deactivate method when it deactivates the object. You can use this method to free resources held by the object. The Deactivate method is also useful for objects that support pooling. Like the Activate method, the Deactivate method has access to the object context.

Object Pooling and Recycling

Objects that support the IObjectControl interface can participate in object recycling and pooling, which can increase the efficiency of activation and deactivation. After MTS calls the Deactivate method, it calls the CanBePooled method, allowing the object to be pooled for reuse. If the object returns TRUE, the object is added to the object pool. Objects that return FALSE or that do notsupport the IObjectControl interface are destroyed.

On activation, MTS uses an object from the pool if one is available. Only if the pool is empty will it use the component's class factory to create a new object.

Components that support object pooling must ensure that an object activated using an object from the pool is indistinguishable to the client from an object that is activated by creating a new object. Component developers must provide the appropriate code in the Activate and Deactivate method implementations to ensure this behavior.

The following table summarizes MTS run-time actions for client call processing.

IObjectControl implemented by component

 

No

Yes

Step 1
Activate the object if needed (just-in-time activation).

Use the component class factory to create an object.

Allocate an object from the pool. If pooling is not supported or the pool is empty, then use the component class factory to create an object.

 

 

Call the object's Activate method.

Step 2
Execute the call.

Call the object method.

Call the object method.

Step 3
Deactivate the object if requested (SetComplete or SetAbort called before return).

 

Call object's Deactivate method

 

Release the last reference held by the MTS run-time environment.

If the system supports pooling, then call CanBePooled. If it returns TRUE, then add the object to the pool. Otherwise, release the last reference held by the MTS run-time environment.

Important: Object pooling and recycling is not available in this release. MTS calls CanBePooled as described here, but no pooling takes place. This forward-compatibility encourages developers to use CanBePooled in their applications now in order to benefit from a future release without having to modify their applications later.

MTS Clients

An application or object that uses an MTS object is referred to as a client of the object. It is important to understand that this is a relative term, and describes a relationship with a specific object. For example, when MTS object A uses MTS object B, the object A, while still an object, is also a client.

Clients that run outside the direct control of the MTS run-time environment are referred to as baseclients.

vi0409

Clients and base clients: The Order object is a client of the Payment object

Base Clients

Base clients are the primary consumers of MTS objects, although base clients execute outside of the MTS run-time environment. One common role of a base client is to provide the application's user interface, and to map the end-user's requests to the business functions exposed by the MTS components.

You can create base clients with a variety of programming languages, including Microsoft® Visual C++®, Microsoft Visual Basic®, Microsoft Visual J++™, COBOL, and even Transact SQL. Base client programs can execute in a variety of environments, from application processes to general system services such as Microsoft Internet Information Server (IIS) or SQL Server. In fact, you can use any programming environment in which you can create COM objects and invoke methods on them.

Base Clients vs. MTS Components

The following table contrasts MTS components with base client applications.

MTS components

Base clients

MTS components are contained in COM dynamic-link libraries (DLLs); MTS loads DLLs into processes on demand.

Base clients can be written as executable files (.exe) or dynamic-link libraries (.dll); MTS is not involved in their initiation or loading.

MTS manages server processes that host MTS components.

MTS does not manage base client processes.

MTS creates and manages the threads used by components.

MTS does not create or manage the threads used by base client applications.

Every MTS object has an associated context object. MTS automatically creates, manages, and releases context objects.

Base clients do not have implicit context objects. They can use transaction context objects, but they must explicitly create, manage, and release them.

MTS objects can use resource dispensers. Resource dispensers have access to the context object, allowing acquired resources to be automatically associated with the context.

Base clients cannot use resource dispensers.

Activities

An activity is a set of objects executing on behalf of a base client application. Every MTS object belongs to one activity. This is an intrinsic property of the object and is recorded in the object's context. The association between an object and an activity cannot be changed. An activity includes the MTS object created by the base client, as well as any MTS objects created by that object and its descendants. These objects can be distributed across one or more processes, executing on one or more computers.

For example, an online banking application may have an MTS object dispatch credit and debit requests to various accounts, each represented by a different object. This dispatch object may use other objects as well, such as a receipt object to record the transaction. This results in several MTS objects that are either directly or indirectly under the control of the base client. These objects all belong to the same activity.

MTS tracks the flow of execution through each activity, preventing inadvertent parallelism from corrupting the application state. This feature results in a single logical thread of execution throughout a potentially distributed collection of objects. By having one logical thread, applications are significantly easier to write.

Whenever you use CoCreateInstance or its equivalent to create an MTS object, a new activity is created; note that this includes a base client creating a transaction context object.

When an MTS object is created from an existing context, using either a transaction context object or an MTS object context, the new object becomes a member of the same activity. In other words, the new context inherits the activity identifier of the context used to create it.

MTS only allows a single logical thread of execution within an activity. This is similar in behavior to a COM apartment, except that the objects can be distributed across multiple processes. When a base client calls into an activity, all other requests for work in the activity (such as from another client thread) are blocked until after the initial thread of execution returns back to the client.

Callbacks and Reentrancy

While MTS does not allow multiple threads of execution within an MTS object, reentrancy is possible via callbacks. Suppose you have an object that creates another object. If the creating object passes a reference to itself to the created object, either directly or indirectly, cycles can occur in the call graph. MTS objects that do this must be prepared to receive a method invocation while blocked waiting for a call to complete. MTS ensures that the incoming call belongs to the same logical thread by using the COM logical thread identifier. COM uses the logical thread identifier for a similar purpose in apartment objects.

Limitation

While no parallel execution can exist within the activity on any individual computer, the MTS run-time environment does not protect against clients entering into the same activity through objects on two different computers. This can result in two parallel threads of execution on different computers. However, if the thread of execution on one computer calls an object in the same activity on the other, the call will be blocked.

This behavior is based on the belief that the cost outweighs the benefits of providing fully distributed activity protection, both in terms of development and run-time performance.

Components and Threading

The MTS run-time environment manages threads for you. MTS components need not, and, in fact, should not, create threads. Components must never terminate a thread that calls into a DLL.

Every MTS component has a ThreadingModel Registry attribute, which you can specify when you develop the component. This attribute determines how the component's objects are assigned to threads for method execution. You can view the threading-model attribute in the MTS Explorer by clicking the Property view in the Components folder. The possible values are Single, Apartment, and Both.

Single-Threaded Components

All objects of a single-threaded component execute on the main thread. This is compatible with the default COM threading model, which is used for components that do not have a ThreadingModel Registry attribute.

The main threading model provides compatibility with COM components that are not reentrant. Because objects always execute on the main thread, method execution is serialized across all objects in the component. In fact, method execution is serialized across all components in a process that uses this policy. This allows components to use libraries that are not reentrant, but it has very limited scalability.

Limitations for Single-Threaded Components

Single-threaded, stateful components are prone to deadlocks. You can eliminate this problem by using stateless objects and calling SetComplete before returning from any method.

The following scenario describes how such a deadlock can occur. Suppose you have a single-threaded Account component, which is written to be both transactional and stateful. The following scenario describes how two clients, Client 1 and Client 2, could call objects in a way that causes an application deadlock:

  • Client 1 creates Account object A and calls it to update an account record. The database update is done under object A's transaction (Transaction 1). Because the object does not call SetComplete before returning to the client, Transaction 1 remains active.

  • Next, Client 2 creates Account object B and calls it to update the same account. Because its work is done under a different transaction (Transaction 2), the attempt to update the account record will block while waiting for Transaction 1 to complete.

  • Client 1 makes another call to object A, for instance, to have it make another change to the account record and complete the transaction. However, the call must wait for the main thread, which is still busy servicing the call from Client 2.

  • The two clients are now deadlocked. Client 1 holds a lock on the account record, while waiting for the server's main thread so that it can complete Transaction 1. Client 2 holds the server's main thread, while waiting for Transaction 1 to complete so that it can update the account record.

Note that this is not a deadlock from the SQL perspective because A's and B's connections are in different transactions.

Apartment-Threaded Components

Each object of an apartment-threaded component is assigned to a thread its apartment, for the life of the object; however, multiple threads can be used for multiple objects. This is a standard COM concurrency model. Each apartment is tied to a specific thread and has a Windows message pump.

The apartment threading model provides significant concurrency improvements over the main threading model. Activities determine apartment boundaries; two objects can execute concurrently as long as they are in different activities. These objects may be in the same component or in different components.

Mixed Threading Models in a Server Process

Within a server process, objects in the same activity must be registered with compatible threading models. The following combinations are valid:

  • Any combination of apartment, both, or free

  • Single-threaded only

Note that this means that single-threaded MTS components cannot be created by an Active Server Page running in-process with IIS or in-process with an IIS application using process isolation.

Programmatic Security

The MTS security model consists of declarative security and programmatic security. Developers can build both declarative and programmatic security into their components prior to deploying them on a Windows NT® security domain.

Important: Security is not supported on Windows® 95. Note the following application behavior when running MTS on Windows 95:

  • All identities are mapped to "Windows 95".

  • Role configuration is not supported.

  • Checking roles always return success.

Roles are central to the MTS security model. A role is an abstraction that defines a logical group of users. At development time, you use roles to define declarative authorization and programmatic security logic. At deployment time, you bind these roles to specific groups and users.

You can administer package security with the MTS Explorer. This is a form of declarative security, which does no't require any component programming is based on standard Windows NT security.

MTS also allows component applications to implement additional access control programmatically. MTS security is integrated with DCOM and Windows NT security. See the Microsoft Platform SDK for further information on COM security APIs.

Basic Security Methods

The IObjectContext interface provides two methods for basic programmatic security:

  • IsCallerInRole

  • IsSecurityEnabled

The key to understanding how MTS security works is to understand roles, as discussed in the following sections.

Roles from a Development Perspective

A role is a symbolic name that defines a logical group of users for a package of components. For example, an online banking application might define roles for Manager and Teller.

You can define authorization for each component and component interface by assigning roles. For example, in the online banking application, only the Manager may be authorized to perform bank transactions above a certain amount of money.

Roles are defined during application development. These roles are then assigned to specific users at deployment time.

Important: Roles on a dual interface are not enforced when IDispatch (late-binding) is used.

Checking If a Caller Is in a Role

The IsCallerInRole method determines if a caller is assigned to a role. The caller is the direct caller, which is the identity of the process (base client or server process) calling into the current server process.

The following illustration shows an application used to order supplies for a business.

vi0409

You can use roles to determine whether the base client has access to objects in the server process. In this scenario, the server process would check to see if the base client is allowed to place an order. Calling IsCallerInRole on the Order object context checks if the direct caller, which is in this case the base client, is in a given role. Such a role might be Purchaser, to restrict the placing of orders to employees within that role.

Security checks are made when a process boundary is crossed. If the Payment object accesses a database, the access rights to the database are derived from the identity of the server process, not the base client. The database would use its own proprietary authorization checking.

Server-process security does not use impersonation. IsCallerInRole has the same semantics regardless of how many calls have taken place within the server process. The identity of the direct caller is always used to make the check. For more information on impersonation, see Advanced Security Methods.

Security for In-Process Components

Because the level of trust is process-wide, running MTS components in-process is not recommended for secure applications. Access checks are not made on calls between components in the same server process. Configuring MTS components to run in-process with the base client gives the base client access to all components within that server process.

The IsSecurityEnabled method determines if security checking is enabled. This method returns FALSE when running in-process. IsSecurityEnabled can be a useful check to make before using IsCallerInRole. IsCallerInRole will always return TRUE when called on an object that is running in-process, which may have unintended effects.

When an MTS component is part of a Library package (in-process), it effectively becomes part of the hosting Server package that creates it. If you create Library packages with components that call IsCallerInRole, you should instruct installers of your Library packages to define the Library package's roles on the hosting Server package. Otherwise, IsCallerInRole will always fail.

Advanced Security Methods

MTS objects can use the ISecurityProperty interface to obtain security-related information from the object context, including the identity of the client that created the object, as well as the identity of the current calling client. Applications can use this information to implement custom access control, such as using the Win32 security interfaces.

Note: Visual Basic programmers can use the SecurityProperty object. The methods for SecurityProperty return user name strings instead of security identifiers (SIDs).

Security Identifiers (SIDs)

A Windows NT security identifier (SID) is a unique value that identifies a user or group. You can use SIDs to determine the exact identity of a user. Because of their uniqueness, SIDs do not have the flexibility of roles.

Callers and Creators

The following figure shows which SIDs are returned by the various methods on ISecurityProperty after a certain sequence of method calls.

Cc723271.vi0410(en-us,TechNet.10).gif

Calling the following methods on Object Y returns SIDs associated with these users:

  • GetDirectCallerSID returns the SID associated with User B.

  • GetDirectCreatorSID returns the SID associated with User B.

  • GetOriginalCallerSID returns the SID associated with User D.

  • GetOriginalCreatorSID returns the SID associated with User A.

Impersonation

Impersonation allows a thread to execute in a security context different from that of the process that owns the thread. Consider the following application scenario.

vi0409

Basic Security Methods described an order-entry scenario in which the base client represents an employee submitting an order. In this scenario, the client is not authorized to use the Payment object and its associated database directly.

Suppose the base client were a report writer for an accounting program. In this case, you want to allow access to the Payment object's database. One way to accomplish this is for the Order object to impersonate the base client, allowing the database to use its own security checking to determine access privileges.

MTS does not promote the use of impersonation, but encourages role-based security. Security is simplified by the single-level of authorization provided by MTS, whereas the impersonation model has an n-level authorization architecture. The report-writer scenario can be simplified by defining a role, such as Accountant, to allow access to the database.

Error Handling

Fault Isolation and Failfast

MTS performs extensive internal integrity and consistency checks. If MTS encounters an unexpected internal error condition, it immediately terminates the process. This policy, called failfast, facilitates fault containment and results in more reliable and robust systems.

Consider a case in which MTS detects that one of its data structures is in a corrupted state. At this point, both the cause and the magnitude of the corruption are unknown. Unfortunately, MTS cannot tell how far the damage has spread. Certainly MTS is in an indeterminate state. But it does not run in isolation. Like other DLLs, it is hosted in a process environment and shares a single address space with the main program executable and many other DLLs. Consequently, it is safe to assume that the entire process has been corrupted. The process is immediately terminated to prevent it from spreading potentially corrupted information to other processes or, worse yet, from allowing corrupted data to be committed and made durable.

As a developer or administrator, you should inspect the Windows NT Event Viewer Application Log for details on any failfast or serious application errors.

Exceptions in MTS Objects

MTS does not allow exceptions to propagate outside of a context. If an exception occurs while executing within an MTS context and the application doesn't catch the exception before returning from the context, MTS catches the exception and terminates the process. Using the failfast policy in this case is based on the assumption that the exception has put the process into an indeterminate state—it is not safe to continue processing.

MTS Object Method Error Return Codes

MTS never changes the value of an HRESULT error code, such as E_UNEXPECTED or E_FAIL, returned by an MTS object method.

When an MTS object returns an HRESULT status code, such as S_OK or S_FALSE, MTS may convert the status code into an MTS error code before it returns to the caller. This occurs, for example, when the application returns S_OK after calling SetComplete; if the object is the root of an automatic transaction that fails to commit, the HRESULT is converted to CONTEXT_E_ABORTED.

When MTS converts a status code to an error code, it clears all of the method's output parameters. Returned references are released and the values of the returned object pointers are set to NULL.