Question

Does anyone know of any cases when using a transaction scope the transaction is escalated to the DTC when multiple connections are NOT open.

I am aware that if I open multiple connections(no matter what connection string) within a transaction scope that the transaction will most likely be promoted to the DTC.

Knowing this I have gone to great lengths to make sure there is only ONE connection opened within my transactions.

However, I have a client where they are getting the exception

An error has occurred. Csla.DataPortalException: DataPortal.Update failed (The underlying provider failed on Open.) ---> Csla.Reflection.CallMethodException: EditableCategory.DataPortal_Update method call failed ---> System.Data.EntityException: The underlying provider failed on Open. ---> System.Transactions.TransactionManagerCommunicationException: Network access for Distributed Transaction Manager (MSDTC) has been disabled. Please enable DTC for network access in the security configuration for MSDTC using the Component Services Administrative tool. ---> System.Runtime.InteropServices.COMException: The transaction manager has disabled its support for remote/network transactions.

Again, I am pretty sure there is only one connection opened within the scope. Take a look.

 protected override void DataPortal_Update()
    {
        using (System.Transactions.TransactionScope ts = new System.Transactions.TransactionScope(System.Transactions.TransactionScopeOption.Required, System.Transactions.TransactionManager.MaximumTimeout))
        {
            //get the dal manager he knows which dal implementation to use
            using (var dalMgr = DataAccess.BusinessObjectsDalFactory.GetManager())
            {
                //get the category dal implementation
                var ecDal = dalMgr.GetProvider<DataAccess.BusinessObjectDalInterfaces.ICategoryDAL>();

                //assume all the data is good at this point so use bypassproperty checks
                using (BypassPropertyChecks)
                {
                    var catData = new Models.Category { CategoryId = CategoryId, CategoryName = CategoryName, LastChanged = TimeStamp };

                    ecDal.UpdateCategory(catData);

                    TimeStamp = catData.LastChanged;
                }
            }

            ts.Complete();
        }

        base.DataPortal_Update();
    }

public class DalManager : Core.Sebring.DataAccess.IBusinessObjectsDalManager {private static string _typeMask = typeof(DalManager).FullName.Replace("DalManager", @"{0}");

public T GetProvider<T>() where T : class
{
  var typeName = string.Format(_typeMask, typeof(T).Name.Substring(1));
  var type = Type.GetType(typeName);
  if (type != null)
    return Activator.CreateInstance(type) as T;
  else
    throw new NotImplementedException(typeName);
}

public Csla.Data.DbContextManager<DataContext> ConnectionManager { get; private set; }

public DalManager()
{
    ConnectionManager = Csla.Data.DbContextManager<DataContext>.GetManager();
}

public void Dispose()
{
  ConnectionManager.Dispose();
  ConnectionManager = null;
}


public void UpdateDataBase()
{
    DatabaseUpgrader.PerformUpgrade();
}
}

 public void UpdateCategory(Models.Category catData)
    {
        if (catData == null) return;
        using (var cntx = DbContextManager<DataContext>.GetManager())
        {
            var cat = cntx.DbContext.Set<Category>().FirstOrDefault(c => c.CategoryId == catData.CategoryId);

            if (cat == null) return;

            if (!cat.LastChanged.Matches(catData.LastChanged))
                throw new ConcurrencyException(cat.GetType().ToString());

            cat.CategoryName = catData.CategoryName;
            //cntx.DbContext.ChangeTracker.DetectChanges();
            cntx.DbContext.Entry<Category>(cat).State = System.Data.EntityState.Modified;
            cntx.DbContext.SaveChanges();
            catData.LastChanged = cat.LastChanged;
        }

    }

The code for DBContextManager is available, but in short it just makes certain there is only one DBContext, and hence one connection opened. Am I overlooking something? I guess its possible that something is up with DBConextManager, so I have posted in the CSLA forums as well(DBContextManager is a part of CSLA). But has anyone run into scenarios where they are sure one connection is opened within the transaction scope and the transaction is escalated to the DTC?

Of course I cannot reproduce the exception on my local dev machine OR any of our QA machines.

Any help is appreciated.

Thanks.

Was it helpful?

Solution

Entity Framework can randomly try to open a new connection when doing transactions with System.Transactions.TransactionScope

Try adding a finally statement and dispose your transaction, also call your dbContext and manually close the connection , this will lesser the ammount of times the transaction gets escalated but it might still happen:

finally
            {
                cntx.Database.Connection.Close();
                transaction.Dispose();
            }

Its a known "bug" you can find more here :

http://petermeinl.wordpress.com/2011/03/13/avoiding-unwanted-escalation-to-distributed-transactions/

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top