I'm using Entity Framework to connect to a MySql database. Some long queries are timing out and then my application crashes. In event viewer I see this exception:
Application: MyApp.exe
Framework Version: v4.0.30319
Description: The process was terminated due to an unhandled exception.
Exception Info: System.NullReferenceException
Stack:
at MySql.Data.MySqlClient.NativeDriver.ExecutePacket(MySql.Data.MySqlClient.MySqlPacket)
at MySql.Data.MySqlClient.NativeDriver.SendQuery(MySql.Data.MySqlClient.MySqlPacket)
at MySql.Data.MySqlClient.Driver.SendQuery(MySql.Data.MySqlClient.MySqlPacket)
at MySql.Data.MySqlClient.Statement.ExecuteNext()
at MySql.Data.MySqlClient.PreparableStatement.ExecuteNext()
at MySql.Data.MySqlClient.PreparableStatement.Execute()
at MySql.Data.MySqlClient.MySqlCommand.ExecuteReader(System.Data.CommandBehavior)
at MySql.Data.MySqlClient.MySqlCommand.ExecuteNonQuery()
at MySql.Data.MySqlClient.MySqlTransaction.Rollback()
at MySql.Data.MySqlClient.MySqlTransactionScope.Rollback(System.Transactions.SinglePhaseEnlistment)
at MySql.Data.MySqlClient.MySqlPromotableTransaction.System.Transactions.IPromotableSinglePhaseNotification.Rollback(System.Transactions.SinglePhaseEnlistment)
at System.Transactions.DurableEnlistmentAborting.EnterState(System.Transactions.InternalEnlistment)
at System.Transactions.DurableEnlistmentActive.InternalAborted(System.Transactions.InternalEnlistment)
at System.Transactions.TransactionStateAborted.EnterState(System.Transactions.InternalTransaction)
at System.Transactions.TransactionStateActive.Rollback(System.Transactions.InternalTransaction, System.Exception)
at System.Transactions.EnlistableStates.Timeout(System.Transactions.InternalTransaction)
at System.Transactions.Bucket.TimeoutTransactions()
at System.Transactions.BucketSet.TimeoutTransactions()
at System.Transactions.TransactionTable.ThreadTimer(System.Object)
at System.Threading.TimerQueueTimer.CallCallbackInContext(System.Object)
at System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
at System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext, System.Threading.ContextCallback, System.Object, Boolean)
at System.Threading.TimerQueueTimer.CallCallback()
at System.Threading.TimerQueueTimer.Fire()
at System.Threading.TimerQueue.FireNextTimers()
at System.Threading.TimerQueue.AppDomainTimerCallback()
So it looks like it's trying to do a rollback when the command timeouts. During this rollback something seems to fail.
The try-catch around the NonQuery is not catching this exception. My code:
try
{
using (MyDB db = new MyDB())
{
db.Database.CommandTimeout = 240;
using (var tran = new TransactionScope())
{
db.MyTable.AddRange(dataToSave);
db.SaveChanges();
tran.Complete();
}
}
}
catch (Exception ex)
{
monitor.OnException(ex);
}
So how can I catch the rollback exception? I don't want my application to crash completely when this happens.
解决方案
Not answering direct problem - but it doesn't look that you use TransactionScope correctly. It looks that purpose of TransactionScope is one level above regular database transactions. E.g. see example in .net docs
You probably better to use plain regular database transactions to avoid similar issues.