3

I want to make transactional a series of sp call (sql server 2005) in a .net 2.0 project surrounding some piece of business logic with a

using(TransactionScope...)

Unluckily, I inherited the DAL from another project and I don't want to make many changes there..the problem is that every method that calls a stored procedure opens a new connection.

So, my question is: is there a way to retrieve the connection used by current transaction i.e. from Transaction.Current??

Thank you

s.

UPDATE: Please tell me what it's wrong with this console application (vs2005, .net 2.0, Sql server 2005)

using System;
using System.Collections.Generic;
using System.Text;
using System.Data.SqlClient;
using System.Data;
using System.Transactions;

namespace ConsoleApplication1
{
    public class Program
    {
        static void Main(string[] args)
        {
            using (TransactionScope ts = new TransactionScope(TransactionScopeOption.Required))
            {
                Console.WriteLine("1");
                test();
                Console.WriteLine("2");
                test();
            }
            Console.WriteLine("END");
        }

        public static void test()
        {
            string connectionString = @"Persist Security Info=True;User ID=usr123;Password=123;Initial Catalog=db123;Data Source=myserver\myinstance;Connect Timeout=180;";

            using (SqlConnection conn = new SqlConnection(connectionString))
            {
                conn.Open();
            }
        }
    }
}
socste2
  • 98
  • 1
  • 7
  • If you're within a TransactionScope, you can just create your own connection, which will (automatically, I believe) enlist itself in the Scope. Is that what you're trying to avoid? – Matt Mills Sep 15 '10 at 15:27
  • Yes, every method in the DAL is istantiating a new SqlConnection("querystring.."), Open()ing it, and then using it in the SqlCommand.. probably the problem is that each method is also Dispose()ing or Close()ing it! – socste2 Sep 15 '10 at 15:53
  • So you're trying to get ahold of the connection that the TransactionScope itself is using to communicate with the database? – Matt Mills Sep 15 '10 at 15:56
  • Exactly.. or I have to code some kind of connectionholder like the one there is in ms enterprise library data access application block.. – socste2 Sep 15 '10 at 16:07

3 Answers3

1

OK, a couple of things:

  1. I doubt that TransactionScope even has the notion of a (Sql)Connection. The reason is that it does work with all sorts of transactional resources, whether that is a dabatabase, a message queuing system, or whatever. You may want to refer to the MSDN Docs for System.Transactions for more information. So, I guess your approach is doomed to fail to begin with.

  2. In your example you're missing the "ts.Complete()" call on, thus your (distributed) transaction will always be rolled back when the using-Scope ends. Now, this has nothing to do with the problem you're describing, but it's worth pointing out nevertheless.

  3. Your ambient transaction, the one resembled by the TransactionScope-instance, is propagated to a distributed transaction because you are using more than one connection inside of it. So essentially, the DTC on your system needs to talk with the DTC on the database server. For that to work both must be configured correctly.

To configure the DTC run the "Component Services" Management Console by executiong C:\Windows\System32\com\comexp.msc. In the treeview navigate to "Component Services\Computers\My Computer". In the context menu open the "Properties". In the properites dialog select the "MSDTC" tab, on it click the "Security Configuration..." button.

In the dialog make sure that the following options are selected:

  • "Network DTC Access"
  • "Allow Remote Clients"
  • "Allow Inbound"
  • "Allow Outbound"
  • "Enable Transaction Internet Protocol (TIP) Transactions"
  • "Enable XA Transactions"

(Note: Some of these might not actually be necessary, YMMV)

You may also want to to set to "No Authentication Required", depending on your local policies/requirements.

You need to do this on both systems: the one your app runs on, and the one with the database.

Christian.K
  • 47,778
  • 10
  • 99
  • 143
  • Ok, thank you anyway.. ..I'll do some refactoring to keep track of the single SqlConnection inside the TransactionScope.. – socste2 Sep 16 '10 at 07:47
1

Ok, thank you all.. ..finally I ended writing something like the Microsoft.Practices.EnterpriseLibrary.Data.TransactionScopeConnections you can find in Enterprise Library (http://entlib.codeplex.com )..

socste2
  • 98
  • 1
  • 7
0

Unless you're not using connection pooling for some reason, there's no good reason not to just new up a SqlConnection and go to town. Let the runtime handle the details of making that performant - that's what connection pooling is for. It sounds like the DAL is written correctly:

...
using (var conn = new SqlConnection("connection string"))
{
    using (var cmd = conn.CreateCommand())
    {
        conn.Open();
        //Do stuff with cmd
    }
}
....

Even if you're not using connection pooling (and you have a valid reason for not doing so), the best path is likely still going to be to new up a SqlConnection and go to town. You don't want to accidentally do something like close the connection the transaction scope is using, and that's assuming it actually has a SqlConnection for you to reference.

Matt Mills
  • 8,692
  • 6
  • 40
  • 64
  • ok, I edited the question with the code I'm using to test it.. when it tries to conn.Open() for the second time, I get the exception "Network access for Distributed Transaction Manager (MSDTC) has been disabled..." – socste2 Sep 15 '10 at 16:59
  • Are you connecting to a remote database server, or an instance on your local machine? – Matt Mills Sep 15 '10 at 17:09
  • It's a remote server.. Microsoft SQL Server Standard Edition, version 9.00.4035.00 – socste2 Sep 15 '10 at 17:17
  • You'll have to go through the DTC setup process to make sure DTC can manage transactions between your web server to your sql server. There's some OS setup involved with that, and possibly firewall setup, but there's lots of good information available on google. – Matt Mills Sep 15 '10 at 17:25
  • This morning I tried the same code with .net 3.5 and sqlserver2008 and the behavior was what Ioriginally expected: two separate connections enlisted in the same transaction without escalating to distribute transaction.. I think the most complete answer to TransactionScope issues is this one: http://stackoverflow.com/questions/2884863/under-what-circumstances-is-an-sqlconnection-automatically-enlisted-in-an-ambient – socste2 Sep 16 '10 at 06:15
  • I'd be surprised (but not shocked) if you told me that the sql server 2008 instance was also on a different machine from where the application is running. Is that the case? – Matt Mills Sep 16 '10 at 06:56
  • Yes: I'm just opening connections from my vs2005 / vs2008 to a remote sql server 2005 / sql server 2008 machine.. ..I have no control over those servers, only a db user account.. – socste2 Sep 16 '10 at 07:39
  • There's a large number of variables here. DTC requires specific ports to be accessible on both machines, as well proper setup on both sides. Is there a firewall between your machine and the SQL 2005 box that is not between your machine and the SQL 2008 box? If you don't control the SQL 2005 box, you should verify with the administrator that DTC is set up and turned on, and that the user your box is trying to manage DTC with has permissions to manage DTC against that box. – Matt Mills Sep 16 '10 at 13:38