Microsoft KB Archive/316627

= HOW TO: Perform a Distributed Transaction with a .NET Provider by Using ServicedComponent in Visual Basic .NET =

Article ID: 316627

Article Last Modified on 9/4/2003

-

APPLIES TO


 * Microsoft .NET Framework 1.1 Service Pack 1
 * Microsoft ADO.NET 1.1
 * Microsoft .NET Framework Service Pack 2
 * Microsoft Enterprise Services (included with the .NET Framework 1.1)
 * Microsoft Visual Basic .NET 2002 Standard Edition
 * Microsoft Visual Basic .NET 2003 Standard Edition

-



This article was previously published under Q316627



For a Microsoft Visual C# .NET version of this article, see 316247.

This article refers to the following Microsoft .NET Framework Class Library namespaces:
 * System.Data.SqlClient
 * System.EnterpriseServices
 * System.Runtime.CompilerServices
 * System.Reflection

IN THIS TASK
SUMMARY
 * Requirements
 * Overview
 * Create Project

REFERENCES



SUMMARY
This step-by-step article demonstrates how to perform a distributed transaction by using a .NET provider with the ServicedComponent class. Although this article uses the SqlClient .NET provider against a Microsoft SQL Server server, you can also use the ODBC or OLE DB .NET managed provider.

back to the top

Requirements
The following list outlines the recommended hardware, software, network infrastructure, and service packs that are required:
 * Microsoft Windows 2000 Professional, Windows 2000 Server, Windows 2000 Advanced Server
 * Microsoft Visual Studio .NET
 * Microsoft SQL Server 7.0 or SQL Server 2000

back to the top

Overview
Instances of a .NET Framework class can participate in an automatic transaction if you prepare the class to do this. Each resource that a class instance or an object accesses enlists in the transaction. For example, if an object uses ADO.NET to post money on an account in a database, the resource manager for the database determines whether the object runs in a transaction. If the object should run in a transaction, the resource manager automatically enlists the database in the transaction.

Use the following process to prepare a class to participate in an automatic transaction:   Apply the TransactionAttribute class to your class to specify the automatic transaction type that the component requests.

The transaction type must be a member of the TransactionOption enumeration. For example:  Public Class Bar Inherits ServicedComponent '. . . End Class  Derive your class from the ServicedComponent class. ServicedComponent is the base class of all classes that use COM+ services. Sign the assembly with a strong name to make sure that the assembly contains a unique key pair. Register the assembly that contains your class with the COM+ catalog.

NOTE: If the client that calls an instance of your class is managed by the common language runtime, the registration is performed for you. This step is required only if an unmanaged caller creates and calls instances of your class. Use the .NET Services Installation Tool (Regsvcs.exe) to manually register the assembly.

For more information about how to sign an assembly with a strong name, see the following topic in the Microsoft .NET Framework Developer's Guide:

Signing an Assembly with a Strong Name

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconassigningassemblystrongname.asp

For more information about this process, see the following topic in the .NET Framework Developer's Guide:

Automatic Transactions and .NET Framework Classes

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/cpguide/html/cpconautomatictransactionsnetframeworkclasses.asp

back to the top

Create the Project
 Follow these steps to create a new Console Application project in Visual Basic .NET:  Start Visual Studio .NET.</li> On the File menu, point to New, and then click Project.</li> In the New Project dialog box, click Visual Basic Projects under Project Types, click Console Application under Templates, and then click OK.</li> In Solution Explorer, rename the Module1.vb file as DistributedTransaction.vb.</li></ol> </li> Delete all of the code from the DistributedTransaction.vb file.</li> On the Project menu, click Add Reference, and then add the following references: <ul> System.EnterpriseServices</li> System.Data.dll</li></ul>

</li>  Add the following code to the DistributedTransaction.vb file: Imports System Imports System.Data.SqlClient Imports System.EnterpriseServices Imports System.Runtime.CompilerServices Imports System.Reflection

<Assembly: ApplicationName(&quot;DistributedTransaction&quot;)> <Assembly: AssemblyKeyFileAttribute(&quot;..\..\DistributedTransaction.snk&quot;)>

Namespace DistributedTransaction

'    'Summary description for Class1. '    Module Module1 '        'The main entry point for the application. '        <STAThread> Sub Main Try Dim myDistributedTran As New DistributedTran myDistributedTran.TestDistributedTransaction Catch e As System.Data.SqlClient.SqlException System.Console.WriteLine(&quot;Transaction Aborted: Error returned: &quot; + e.Message) End Try End Sub End Module

'    'Summary description for TestApp. '    <Transaction(TransactionOption.Required)> Public Class DistributedTran Inherits ServicedComponent

Public Sub DistributedTran End Sub

<AutoComplete> Public Function TestDistributedTransaction As String

'The following Insert statement goes to the first server. 'This Insert statement does not produce any errors. Dim insertCmdSql As String = &quot;Insert Into TestTransaction (Col1, Col2) Values (1,'Sql Test')&quot;

'The following Delete statement goes to the second server. 'Because the table does not exist, this code throws an exception. Dim exceptionCausingCmdSQL As String = &quot;Delete from NonExistentTable&quot;

'The following connection strings create instances of two SqlConnection objects 'to connect to two different SQL Server servers in your environment. 'Modify the connection strings as necessary for your environment. Dim SqlConn1 As New SqlConnection(&quot;Server=Name_of_Server1;uid=User_Id;database=DatabaseName;pwd=Password&quot;) Dim SqlConn2 As New SqlConnection(&quot;Server=Name_of_Server2;uid=User_Id;database=DatabaseName;pwd=Password&quot;)

Try Dim insertCmd As New SqlCommand(insertCmdSql, SqlConn1) Dim exceptionCausingCmd As New SqlCommand(exceptionCausingCmdSQL, SqlConn2)

'This command should run properly. insertCmd.Connection.Open insertCmd.ExecuteNonQuery

'This command results in an exception, which automatically rolls back 'the first command (the insertCmd command). exceptionCausingCmd.Connection.Open Dim cmdResult As Integer = exceptionCausingCmd.ExecuteNonQuery

SqlConn1.Close SqlConn2.Close

Console.WriteLine(&quot;Hello&quot;)

Catch ex As System.Data.SqlClient.SqlException

'After you catch the exception in this function, throw it. 'The service component receives this exception and aborts the 'transaction. The service component then throws the same exception, 'and the calling function receives the error message. Console.WriteLine(ex.Message) Throw (ex)

Finally

'Close the connection. If SqlConn1.State.ToString = &quot;Open&quot; Then SqlConn1.Close End If

If SqlConn2.State.ToString = &quot;Open&quot; Then SqlConn2.Close End If

End Try

Return &quot;Success&quot;

End Function

End Class End Namespace </li> On the File menu, click Save All.</li> Click Start, point to Programs, point to Microsoft Visual Studio .NET, point to Visual Studio .NET Tools, and then click Visual Studio .NET Command Prompt.</li> Open the folder that contains your project, and then run the following command to sign the assembly with a strong name:

sn -k DistributedTransaction.snk

</li> Build your application.</li>  Create the following table in the first SQL Server server: if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[TestTransaction]') and OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table [dbo].[TestTransaction] GO

CREATE TABLE [dbo].[TestTransaction] (   [Col1] [int] NULL,    [Col2] [varchar] (100) NULL ) ON [PRIMARY] GO </li> Run your application. Notice that you receive the following error message (which is the expected behavior):

Transaction Aborted: Error returned: Invalid object name 'NonExistentTable'.

</li> <li> Open the SQL Server Query Analyzer, add the following code, and then press F5 to run the query: USE NORTHWIND; SELECT * FROM TestTransaction WHERE Col1=1 AND Col2='Sql Test' Note that the query does not return any rows because the transaction was aborted. </li> <li> Locate the following code in your Visual Basic project: Dim exceptionCausingCmdSQL As String = &quot;Delete from NonExistentTable&quot; and replace the SQL statement with a valid query that does not cause the transaction to abort. For example: Dim exceptionCausingCmdSQL As String = &quot;Select @@Identity from customers&quot; </li> <li>Press F5 to compile and to run the application again.</li> <li>Run the command from step 11 in Query Analyzer again. Notice that the query returns a row because the transaction was able to complete successfully.</li></ol>

NOTES:
 * This example does not perform error handling.
 * SQL Server and Microsoft Distributed Transaction Coordinator (MS DTC) must be running on all clients and servers.

back to the top

<div class="references_section">