Introduction
A lot of business logic runs in COM+ classes. There is increasingly a need to expose this logic in WCF services.
When WCF services are configured as web services, any client may access them. WCF services may run in transactions.
When a WCF service calls a COM+ method, this method should run in the same transaction in which the WCF service runs.
This program shows how to do this, as there is little documentation on this subject.
Background
COM+
COM+ functionality in .NET is available under the System.EnterpriseServices namespace. Any class that inherits
the ServicedComponent class runs in COM+. If in addition the class has the attribute Transaction(TransactionOption.Required)
defined, the methods of this class run in a transaction.
WCF
WCF functionality in .NET is available under the System.ServiceModel namespace. A WCF service is implemented
by the ServiceHost class. This class takes as argument in the constructor the class that implements the
methods of the service. The methods that have the attribute OperationBehavior(TransactionScopeRequired:=True) run in a transaction.
Calling COM+ from WCF
A WCF service may instantiate a COM+ class and call a method of that class. However, the COM+ method will run
in a new transaction. In order for the COM+ method to run in the same transaction, the transaction scope's
InteropOption must be set to Full:
Using s As New TransactionScope(Transaction.Current, _
New TimeSpan(0, 1, 0), EnterpriseServicesInteropOption.Full)
End Using
Using the code
This program is a modified version of the Service Transaction Behavior sample of Microsoft's MSDN/Windows
Communication Foundation Samples/Basic/Services/Transaction. The difference is that the data access layer resides in the additional COM+ class, rather in the service.
The solution contains four projects:
- Tests.BE: contains the business entities, in this case only the typed dataset
LogData. The post build events register the assembly in GAC.
- Tests.DA: data-access layer that provides methods to retrieve and update records in a database.
The post build events register the assembly in GAC and COM+ and copy the config file to the respective GAC folder.
The connection string is defined in the app.config file. The assembly contains
the following classes:
ClassBase: class running in COM+, providing functions to read data from the database and execute SQL statements.
ClassU: class running in COM+, uses or creates a transaction and provides a function to update a table with the changes recorded in a dataset.
- Tests.WS: implements the WCF service, and contains the following classes:
ICalculator: interface defining service methods. Example:
<OperationContract()><TransactionFlow(TransactionFlowOption.Mandatory)>Function Add(ByVal n As Double) As Double
CalculatorService: implementation of the service methods. Each method does a calculator operation, and writes a record to the Log table of the database. Example:
<OperationBehavior(TransactionScopeRequired:=True, TransactionAutoComplete:=True)> _
Public Function Add(ByVal n As Double) As Double Implements ICalculator.Add
Using s As New TransactionScope(Transaction.Current, New TimeSpan(0, 1, 0), EnterpriseServicesInteropOption.Full)
RecordToLog([String].Format(CultureInfo.CurrentCulture, "Adding {0} to {1}", n, runningTotal))
runningTotal = runningTotal + n
s.Complete()
End Using
Return runningTotal
End Function
- Program: contains the code to start the service:
host = New ServiceHost(GetType(CalculatorService))
- Tests.UI: Contains a Windows form to test the services. The form provides the button Test WS,
which initiates a WCF transaction, calls sequentially the four methods of the service, and if
successful, marks the transaction as completed.
Dim client As New Tests.PX.CalculatorClient()
Using tx As New TransactionScope()
client.Add(100)
...
tx.Complete()
End Using
client.Close()
To run the program, follow these steps:
- With Visual Studio 2010 open solution Tests.WCFTrans.sln.
- Set the connection string in app.config. It must be a valid connection string to a database, on which you have the right to create tables.
- Build the solution. If it fails, check that the paths to gacutil and regsvcs in the post-build events are correct.
- You may look in Component Services (Start/Run/comexp.msc) that the COM+ application Tests.DA has been created.
- Start the WCF service by double-clicking on Tests.WS\bin\Tests.WS.exe. This will open a command window like the following:

- Start Tests.UI either in the debugger, or with the executable Tests.UI\Debug\bin\Tests.UI.exe. The following form appears:

- Choose the menu item File/Recreate tables. This will create the tables Log and Log_Audit on the database.
- Press the button Test WS. This will initiate a transaction, call the four methods of the service, and
complete the transaction. If successful, the following will be displayed:

- To check that the data was written to the database, issue the command:
SELECT * FROM Log on the database.
- Stop the service by pressing Enter in the command window mentioned before.
- To check that no data is saved when an error occurs in the service, add the following code in the
Divide method of the class CalculatorService before the statement
s.Complete():
Throw New Exception("Ex1")
- Recompile Tests.WS, and start it as before.
- In the form of Tests.UI click the button Test WS again. An error message will appear.
- Check that no data was written to the database as before with the same SQL command. The reason is that
although the service methods
Add, Delete, and Multiply
are completed successfully and wrote to the database, the service
method Divide fails. The transaction is aborted and the changes in the database
are rolled back.
- You may throw an exception in COM+, as well as in the UI, and the data is rolled back.
History
- WCF service CalculatorService uses COM+. UI initiates transaction and calls the service.