Configuring Transactions

Now that you understand what transactions are and what they are good for and have reviewed the COM+ transaction architecture, it is time to put that knowledge into practice to build and configure transactional components in COM+.

You can use the Component Services Explorer to configure transaction support for your components. Every component has a Transactions tab on its properties page. The tab offers you five options for transaction support (see Figure 4-7): Disabled, Not Supported, Supported, Required, and Requires New. The settings let you control whether instances of your component take part in a transaction and if so, whether and when they should be the root of that transaction.

Configure transaction support for a component on the component’s Transactions tab

Figure 4-7.  Configure transaction support for a component on the component’s Transactions tab

COM+ determines which transaction to place the object in when it creates the object. COM+ bases its decision on two factors: the transaction of the object’s creator and the configured transaction support of the object (actually, for the component that the object is an instance of).

A COM+ object can belong to its creator’s transaction, be a root of a new transaction, or not take part in a transaction. If the object is configured with transaction support Disabled or Not Supported, it will never be part of a transaction, regardless of whether its creator has a transaction or not. If the object is configured with Supported and its creator has a transaction, then COM+ places the object in its creator’s transaction. If the creating object does not have a transaction, then the newly created object will not have a transaction. If the object is configured with transaction support set to Required, then COM+ puts it in its creator’s transaction if the creating object has a transaction. If the creating object does not have a transaction and the object is configured to require a transaction, COM+ creates a new transaction for the object, making it the root of that new transaction. If the object is configured with transaction support set to Requires New, then COM+ creates a new transaction for it, making it the root of that new transaction, regardless whether its creator has a transaction or not. The COM+ transaction allocation decision matrix is summarized in Table 4-1.

Table 4-1.  COM+ transaction allocation decision matrix

Object transactional support

Creator is in transaction

The object will take part in:

Disabled/Not Supported

No

No Transaction

Supported

No

No Transaction

Required

No

New Transaction (will be the root)

Required New

No

New Transaction (will be the root)

Disabled/Not Supported

Yes

No Transaction

Supported

Yes

Creator’s Transaction

Required

Yes

Creator’s Transaction

Required New

Yes

New Transaction (will be the root)

Once COM+ determines what transaction to place the object in, that placement is fixed for the life of the object, until the object is released by the client. If the object is not part of a transaction, it will never be part of one. If the object is part of a transaction, it will always be part of that transaction.

Figure 4-8 shows an example of how objects are allocated to transactions. A client that does not have a transaction creates an object configured to require a transaction. COM+ creates a new transaction for that object (Transaction 1), making it the root of the transaction. The object then creates five more objects, each with a different transaction configuration. The objects configured as Disabled and Not Supported are placed outside Transaction 1. The objects market Supported and Required are placed in Transaction 1. However, the object configured as Requires New cannot share its creator’s transaction, so COM+ creates a new transaction (Transaction 2) for that object.

Allocating objects to transactions based on their configuration and the transaction requirements of the creating object

Figure 4-8.  Allocating objects to transactions based on their configuration and the transaction requirements of the creating object

Transaction Disabled

When you configure a component with transaction support set to Disabled, the component never takes part in any transaction. COM+ also does not consider transactional configuration when deciding on activation context for this component or other components it creates. As a result, the object may or may not share its creator’s context, depending on the configuration of other services.

You should be careful when mixing transactional objects with nontransactional objects, as it can jeopardize isolation and consistency. The nontransactional objects may have errors, but because they are not part of the transaction, they cannot affect transaction outcome (threatens consistency). In addition, the nontransactional objects can act based on information not yet committed (threatens isolation).

The Disabled transaction support setting is useful in two situations. The first situation is when you have no need for transactions. The second is when you want to provide custom behavior and you need to perform your own programmatic transaction support or enlist resources manually. Note that you are not allowed to vote on the outcome of any COM+ transaction; you have to manage your transaction yourself.

Transaction Not Supported

When you configure a component with transaction support set to Not Supported, even though it never takes part in any transaction, COM+ takes into account transactional configuration when deciding on the activation context for this component or other components it creates. As a result, the object shares its creator’s context only if the creating object is also configured with Not Supported.

Not Supported is the default value when importing a classic COM component to COM+. Transaction support set to Not Supported is useful when the operations performed by the component are nice to have, but should not abort the transaction that created them if the operations fail. For example, in the ATM use case, printing the receipt is not a critical operation. The withdrawal transaction should commit and the customer should get the bills even if the ATM was unable to print a receipt. In all other circumstances, transactions configured as Not Supported can jeopardize isolation and consistency when mixed with transactional components, for the same reasons discussed when transaction support is set to Disabled.

Transaction Supported

When you configure a component with transaction support set to Supported, the object joins that transaction if the object’s creating client has a transaction. If the creating object does not have a transaction, the object does not take part in any transaction.

Surprisingly, this awkward setting can be useful. Imagine a situation when you want to propagate a transaction from the creating client of your object to downstream objects your object creates, but your object has no use for transactions itself. If the downstream objects require transaction support and you configure your object to not require a transaction, then the downstream objects will be placed in separate transactions. Setting the transaction support to Supported allows you to propagate the transaction downstream. In all other cases, you should avoid this setting; it can jeopardize consistency and isolation when the creating client does not have a transaction, but the downstream objects you create still require transaction support and are placed in transactions separate from your client.

Even though the component may not have a direct need for transaction support, it still has to abide by transactional component design guidelines (discussed later in this chapter), which may be a liability if it does not require a transaction. Use this setting judiciously.

Transaction Required

When you configure a component with transaction support set to Required, you state to COM+ that your component requires a transaction to operation properly and that you have no objection to sharing your creator’s transaction. If the creating client has a transaction, the object joins it. If the client does not have one, COM+ must create a new transaction for the object, making it the root of the new transaction.

Note that your component’s code should operate identically when it is the root and when it just takes part in a transaction. There is no way your object can tell the difference anyway.

Setting transaction support to Required is by far the most commonly used transaction support setting for transactional components. Of course, the component must adhere to the design requirements of a transactional component.

Transaction Requires New

When you configure a component with transaction support set to Requires New, an instance of your component is always the root of a new transaction, regardless of whether its creating client has a transaction or not. This setting is useful when you want to perform transactional work outside the scope of the creating transaction. Examples would be when you want to perform logging or audit operation or when you want to publish events to subscribers, regardless of whether your creating transaction commits or aborts.

You should be extremely careful when using the Requires New setting. Verify that the two transactions (the creating transaction and the one created for your object) do not jeopardize consistency if one aborts and the other commits.

You can also use Requires New when you want your object to control the duration of the transaction because once that object is released, the transaction ends.

Transaction Support IDL Extension

When you import a COM component into the COM Explorer, COM+ selects Not Supported as the default configuration for your component’s transaction support. However, transaction support is an intrinsic part of your COM+ component design. COM+ components should specify in the IDL file what their required transaction support is, using a dedicated IDL extension. When you import a COM+ component that uses the IDL extension into the Component Services Explorer, COM+ uses the declared transaction support from the component’s type library as the initial value. You can override that value later. For example, if you use the TRANSACTION_REQUIRED attribute on your CoClass definition:

[
   uuid(94072015-7D6B-4811-BDB5-08983088D9C2),
   helpstring("MyComponent Class"),
   TRANSACTION_REQUIRED
]
coclass MyComponent
{
   [default] interface IMyInterface;
};

COM+ selects the Required setting for the component when it is imported to the Component Services Explorer. The following attributes are also available:

  • TRANSACTION_NOT_SUPPORTED

  • TRANSACTION_SUPPORTED

  • TRANSACTION_REQUIRES_NEW

Note that there is no TRANSACTION_DISABLED attribute because that attribute is used mostly when importing existing COM components to COM+. To use these IDL extensions you have to include the mtxattr.h file in your IDL file.

Get COM & .NET Component Services now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.