Bug #54681 "Object reference not set to an instance of an object." with TransactionScope
Submitted: 21 Jun 2010 23:18 Modified: 13 Jul 2010 9:08
Reporter: Shane Marsden Email Updates:
Status: Closed Impact on me:
None 
Category:Connector / NET Severity:S1 (Critical)
Version:6.3.2 OS:Windows (Windows 7 x64)
Assigned to: Vladislav Vaintroub CPU Architecture:Any
Tags: .net, 6.3.2, Connector/Net, IPromotableSinglePhaseNotification, MySqlPromotableTransaction, Object reference not set to an instance of an object, transaction, TransactionScope

[21 Jun 2010 23:18] Shane Marsden
Description:
I get the error and stacktrace below intermittently (more than 1 time in 5) when using a TransactionScope with EF in .NET 4. I have a complete project/script available.

I have classed this as S1 because I don't know of any workaround, but that doesn't mean there isn't one!

Object reference not set to an instance of an object.
   at MySql.Data.MySqlClient.MySqlPromotableTransaction.System.Transactions.IPromotableSinglePhaseNotification.Initialize()
   at System.Transactions.TransactionStatePSPEOperation.PSPEInitialize(InternalTransaction tx, IPromotableSinglePhaseNotification promotableSinglePhaseNotification)
   at System.Transactions.TransactionStateActive.EnlistPromotableSinglePhase(InternalTransaction tx, IPromotableSinglePhaseNotification promotableSinglePhaseNotification, Transaction atomicTransaction)
   at System.Transactions.Transaction.EnlistPromotableSinglePhase(IPromotableSinglePhaseNotification promotableSinglePhaseNotification)
   at MySql.Data.MySqlClient.MySqlConnection.EnlistTransaction(Transaction transaction)
   at MySql.Data.MySqlClient.MySqlConnection.Open()
   at System.Data.EntityClient.EntityConnection.OpenStoreConnectionIf(Boolean openCondition, DbConnection storeConnectionToOpen, DbConnection originalConnection, String exceptionCode, String attemptedOperation, Boolean& closeStoreConnectionOnFailure)
   at System.Data.EntityClient.EntityConnection.Open()
   at System.Data.Objects.ObjectContext.EnsureConnection()
   at System.Data.Objects.ObjectQuery`1.GetResults(Nullable`1 forMergeOption)
   at System.Data.Objects.ObjectQuery`1.System.Collections.Generic.IEnumerable<T>.GetEnumerator()
   at System.Linq.Enumerable.Single[TSource](IEnumerable`1 source)
   at System.Data.Objects.ELinq.ObjectQueryProvider.<GetElementFunction>b__3[TResult](IEnumerable`1 sequence)
   at System.Data.Objects.ELinq.ObjectQueryProvider.ExecuteSingle[TResult](IEnumerable`1 query, Expression queryRoot)
   at System.Data.Objects.ELinq.ObjectQueryProvider.System.Linq.IQueryProvider.Execute[S](Expression expression)
   at System.Linq.Queryable.Count[TSource](IQueryable`1 source)
   at MvcApplication2.Controllers.ThingController.CreateLoads() in C:\dev\MvcApplication2\MvcApplication2\Controllers\ThingController.cs:line 96
   at lambda_method(Closure , ControllerBase , Object[] )
   at System.Web.Mvc.ActionMethodDispatcher.Execute(ControllerBase controller, Object[] parameters)
   at System.Web.Mvc.ReflectedActionDescriptor.Execute(ControllerContext controllerContext, IDictionary`2 parameters)
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor, IDictionary`2 parameters)
   at System.Web.Mvc.ControllerActionInvoker.<>c__DisplayClassd.<InvokeActionMethodWithFilters>b__a()
   at System.Web.Mvc.ControllerActionInvoker.InvokeActionMethodFilter(IActionFilter filter, ActionExecutingContext preContext, Func`1 continuation)

How to repeat:
I have a complete project/script available that demonstrates the problem.
[21 Jun 2010 23:20] Shane Marsden
.NET project demonstrating problem

Attachment: MvcApplication2.zip (application/x-zip-compressed, text), 292.74 KiB.

[21 Jun 2010 23:26] Shane Marsden
To see the error, perform the following:

create database x, username x, password x. run included .sql to create table
web.config assumes db running on localhost

tested on servers 5.1.38 GA and 5.1.48 GA

run project, click Thing tab and click "Create Loads in a Loop". By the second or third time of clicking this link, you should see the error.

Note that I have no problems using 6.2.3 with TransactionScopes under .NET 3.5 (but no EF support there so I don't know if it's anything to do with that).
[24 Jun 2010 9:09] Bogdan Degtyariov
Hi Shane,

Thank you for the detailed description of the problem and the test project.
We shall give it a test and then come back to you with the results.
Thanks.
[28 Jun 2010 6:31] Bogdan Degtyariov
Hi Shane,

I was able to repeat the problem. The Exception was thrown after the 3rd click.
Setting the bug status to verified and trying to find a solution for this bug.
Thanks.
[6 Jul 2010 16:45] Erskine Thompson
Hi Bogdan,

I believe I've traced the bug back to MySqlPromotableTransaction. The private member globalScopeStack is initialized at declaration and marked with the [ThreadStatic] attribute. This will work fine for the first thread but all subsequent threads will run into a null reference. I was able to resolve this issue by  moving the initialization of this member to the InitializeMethod() method.

Here's a copy of the unidiff:
=== modified file MySql.Data/Provider/Source/MySqlPromotableTransaction.cs
--- MySql.Data/Provider/Source/MySqlPromotableTransaction.cs	2010-01-13 22:59:43 +0000
+++ MySql.Data/Provider/Source/MySqlPromotableTransaction.cs	2010-07-06 08:14:18 +0000
@@ -86,7 +86,7 @@
     {
         // Per-thread stack to manage nested transaction scopes
         [ThreadStatic]
-        static Stack<MySqlTransactionScope> globalScopeStack = new Stack<MySqlTransactionScope>();
+        static Stack<MySqlTransactionScope> globalScopeStack;
 
         MySqlConnection connection;
         Transaction baseTransaction;
@@ -136,6 +136,9 @@
            // We need to save the per-thread scope stack locally.
            // We cannot always use thread static variable in rollback: when scope
            // times out, rollback is issued by another thread.
            
            // Bug #54681
+           if(globalScopeStack == null)
+               globalScopeStack = new Stack<MySqlTransactionScope>();
+
            scopeStack = globalScopeStack;
            scopeStack.Push(new MySqlTransactionScope(connection, baseTransaction, 
               simpleTransaction));
[7 Jul 2010 13:37] Bugs System
A patch for this bug has been committed. After review, it may
be pushed to the relevant source trees for release in the next
version. You can access the patch from:

  http://lists.mysql.com/commits/113033

891 Vladislav Vaintroub	2010-07-07
      - Fix null reference exception when TransactionScope is used by multiple threads
       (bug#54681 , fix contributed by Erskine Thompson)
[7 Jul 2010 13:42] Vladislav Vaintroub
@Erskine, yes, this was it.
Thanks a lot for the proposed fix!

pushed to the next 6.3
[13 Jul 2010 9:08] Tony Bedford
An entry has been added to the 6.3.3 changelog:

MySQL Connector/NET generated a null reference exception when TransactionScope was used by multiple threads.