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:
Dim client As New Tests.PX.CalculatorClient()
Using tx As New TransactionScope()
client.Add(100)
...
tx.Complete()
End Using
client.Close()
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:
<OperationContract()><TransactionFlow_
(TransactionFlowOption.Mandatory)>Function Add(ByVal n As Double) As Double
<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
host = New ServiceHost(GetType(CalculatorService))
ICalculator
: interface defining service methods. Example: CalculatorService
: implementation of the service methods. Each method does a calculator operation, and writes a record to the Log
table of the database. Example: - Program: contains the code to start the service:
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.
To run the program, follow these steps:
Throw New Exception("Ex1")
- 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()
: - 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 written 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.