views:

592

answers:

2

I'm getting this error (Distributed transaction completed. Either enlist this session in a new transaction or the NULL transaction.) when trying to run a stored procedure from C# on a SQL Server 2005 database. I'm not actively/purposefully using transactions or anything, which is what makes this error weird. I can run the stored procedure from management studio and it works fine. Other stored procedures also work from C#, it just seems to be this one with issues. The error returns instantly, so it can't be a timeout issue. The code is along the lines of:

  SqlCommand cmd = null;
  try
  {
      // Make sure we are connected to the database
      if (_DBManager.CheckConnection())
      {
        cmd = new SqlCommand();

        lock (_DBManager.SqlConnection)
        {
          cmd.CommandText = "storedproc";
          cmd.CommandType = System.Data.CommandType.StoredProcedure;
          cmd.Connection = _DBManager.SqlConnection;

          cmd.Parameters.AddWithValue("@param", value);

          int affRows = cmd.ExecuteNonQuery();

          ...
        }
      }
      else
      {
        ...
      }
  }
  catch (Exception ex)
  {
    ...
  }

It's really got me stumped. Thanks for any help

A: 

From the code it seems that you are utilizing an already opened connection. May be there's a transaction pending previously on the same connection.

S M Kamran
You appear to be onto something here. If I .Close() and .Open() the connection at the beginning of the call the stored proc works. This hasn't been needed in the past though, and other programs with the same code (but different stored proc) are still working. I can't find any different code between the programs except the stored proc name reader.Dispose(); command.Dispose(); (where appropriate) after each query. Is there any way to ensure transactions are closed?
mrnye
You might wanna check out the store procedure. Are you utilizing any transaction from there... Or is there any triggers on table which will by default to a transaction.
S M Kamran
In a layered architecture it's always better to implements automicity of actions at Business logic layer however most layer designer make their dal methods atomic opening and closing connections on each dal operation. If you want to hang around with one connection problem then open a connection on logic layer and then pass it on to dal.. this way you'll have a track of the connection being used. By the way using a single connection and then allowing synchronous access to it will decrease the performance of your application will be quite drastic I belive.
S M Kamran
Are you sure you are closing connection with in the lock... What if you acquire a lock and another request is waiting for the same lock to finish... The connection can be reclaimed as soon as the lock block is done... so if the connection is not closed then you might have a problem...
S M Kamran
Nothing wrong inside the actual stored proc. No triggers, no transactions. The code has been working fine for a couple of months now, no idea what made it stop working. Connection has been kept open for the duration of the program to allow quicker query speeds. I will swap to trying to utilize the connection pool and see how it goes. I don't really like the idea of handling connection open/closing in the logic, I'd rather it be handled by the DAL which is the way it currently works. Thanks for the suggestions.
mrnye
+1  A: 

It sounds like there is a TransactionScope somewhere that is unhappy. The _DBManager.CheckConnection and _DBManager.SqlConnection sounds like you are keeping a SqlConnection hanging around, which I expect will contribute to this.

To be honest, in most common cases you are better off just using the inbuilt connection pooling, and using your connections locally - i.e.

using(var conn = new SqlConnection(...)) { // or a factory method
    // use it here only
}

Here you get a clean SqlConnection, which will be mapped to an unmanaged connection via the pool, i.e. it doesn't create an actual connection each time (but will do a logical reset to clean it up).

This also allows much more flexible use from multiple threads. Using a static connection in a web app, for example, would be horrendous for blocking.

Marc Gravell
The only reason I keep a single connection is because I do a fairly large amount of inserts and didn't want to have the slowdown of opening connections each time. I will try this method and see how quick it is. Still doesn't explain why this once working code now doesn't work, even though nothing has changed.
mrnye
Oh also, in Activity Monitor in Management Studio I can see 1 under "Open Transactions" for my connection. Which means there is a transaction hanging around. Is there any way to get more details on the open transaction? Thanks for the help.
mrnye
Opened connection(s) will commonly remain in the pool for the app life; killing it fully isn't easy short of disabling pooling, sacrificing the performance benefits of the pool.
Marc Gravell