When would SqlConnection.Open blow away a COM+ context?

Ok, this is pretty crazy. I’m on XPSP2 and I’ve entered a ServiceDomain with a ServiceConfig that requires transactions with an IsolationLevel of Serializable. Now, I’ve done this a million times before and it works no problem. In the specific scenario I’m dealing with now, I’m seeing something completely insane. I’ve constructed a SqlConnection and I’m getting ready to Open it. Up until I call Open ContextUtil.IsInTransaction is true. Now I step over the call to Open and *bam* I’ve lost my COM+ context. ContextUtil.IsInTransaction is now false and any attempt to SetComplete or SetAbort throws:

System.Runtime.InteropServices.COMException (0x8004E004): There is no MTS object context at System.Runtime.InteropServices.Marshal.ThrowExceptionForHR(Int32 errorCode, IntPtr errorInfo) at System.Runtime.InteropServices.Marshal.ThrowExceptionForHR(Int32 errorCode) at System.EnterpriseServices.Thunk.ContextThunk.SetComplete() at System.EnterpriseServices.ContextUtil.SetComplete()

No exceptions are being thrown from within the call to Open and I can run another project that does the same transaction/connection logic to the same SQL server instance, which is just a local instance on my dev box anyway, and use distributed transactions there just fine. Therefore I know my MSDTC is properly configured and working.

I’ve cross-posted this to the ADVANCED DOTNET list that DM hosts. Hopefully I’ll get an answer from someone soon, ’cause I’m completely perplexed by this one.

Update:

Ok, so I dug a little deeper. I found out exactly where inside of SqlConnection::Open that my context is being blown away. Have a look at this:

system.enterpriseservices.dll!System.EnterpriseServices.Platform.Initialize() + 0x117 bytes 
  system.enterpriseservices.dll!System.EnterpriseServices.Platform.get_W2K() + 0x9 bytes 
  system.enterpriseservices.dll!System.EnterpriseServices.ResourcePool.ResourcePool(System.EnterpriseServices.ResourcePool.TransactionEndDelegate cb = {System.EnterpriseServices.ResourcePool.TransactionEndDelegate}) + 0x1b bytes 
  system.data.dll!System.Data.SqlClient.ConnectionPool.CreateResourcePool() + 0x40 bytes 
  system.data.dll!System.Data.SqlClient.ConnectionPool.ConnectionPool(System.Data.SqlClient.DefaultPoolControl ctrl = {System.Data.SqlClient.SqlConnectionPoolControl}) + 0x1c2 bytes 
  system.data.dll!System.Data.SqlClient.PoolManager.FindOrCreatePool(System.Data.SqlClient.DefaultPoolControl ctrl = {System.Data.SqlClient.SqlConnectionPoolControl}, int SID = 2066800) + 0xb8 bytes 
  system.data.dll!System.Data.SqlClient.SqlConnectionPoolManager.GetPooledConnection(System.Data.SqlClient.SqlConnectionString options = {System.Data.SqlClient.SqlConnectionString}, bool isInTransaction = false) + 0x141 bytes 
  system.data.dll!System.Data.SqlClient.SqlConnection.Open() + 0xce bytes 

So what this looks like is, even though I’ve already entered a ServiceDomain prior to opening the SqlConnection, this particular code path somehow reinitializes .NET’s enterprise services layer whacking the current COM+ state. Can you say BUG!? :

Well I’ll go ahead and submit this as a bug, but the workaround would seem to be to open a dummy SqlConnection at the very beginning of your application (perhaps for each distinct connection string since each one creates a new pool??) to force this scenario to happen so that future calls to open a connection, which may actually need the connection to be hooked up, do not have to run through this code path.

0 thoughts on “When would SqlConnection.Open blow away a COM+ context?

  1. I do not. Here’s the exact scenario where I’m seeing this happen:

    We’re running inside a console based, in-house unit testing application. This application’s entry point is *not* marked with any explicit threading model (STA or MTA). To execute the unit tests, this application then spins up a new AppDomain into which it loads the assembly that contains the test fixtures/methods. The various test methods are then executed one at a time inside of this “sandbox” AppDomain. It is inside the execution of one of the test methods that this occurs.

    FWIW, as long as I open a SqlConnection prior to starting the transaction with the ServiceDomain it does work, most likely because your codepath only initializes the enterprise services platform the first time through. In our production application this happens naturally so we don’t have an issue, which is why this bug only cropped up when the test method was written.

  2. If I remember correctly, simply making sure that I explicitly marked the entry point of the unit testing program as [STAThread] took care of the problem.

    HTH,
    Drew

  3. I’m having the same issue. Mine is an asp.net application, application server is a windows 2003 enterprise server and database is in a different windows server. In my environment servicedomain/serviceconfig works fine. But in my client’s place, No MTS Context object is thrown. Client’s place, app server is in windows 2003 server Web edition and database server is in windows 2003 server.

    Can you tell me how you resolved this issue? and can you share the piece of code.

    Thanks

    rajesh

Leave a Reply