DataPortal.Update failed (System.Transactions.TransactionManagerCommunicationException)

DataPortal.Update failed (System.Transactions.TransactionManagerCommunicationException)

Old forum URL: forums.lhotka.net/forums/t/10098.aspx


rmp251 posted on Wednesday, February 23, 2011

Normal 0 false false false EN-CA X-NONE X-NONE

Hi folks,

The following error has been plaguing me for weeks now:

"DataPortal.Update failed (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."

It happens sporadically when business objects are updated. It seems very random, and I think the error message is misleading because I played with the DTC & firewall settings, lifting any and all restrictions, but still no luck – the error message just changed to:

 "Communication with the underlying transaction manager has failed.  The MSDTC transaction manager was unable to pull the transaction from the source transaction manager due to communication problems."

It seems that what triggered this was when I was doing a one-time mass update of objects programmatically, calling DataPortal_Update() on thousands of objects. That's when I started seeing these errors (many updates would succeed and then one would fail with this message, and then I would try again, etc.). What really gets me is that now after those changes were reverted and I'm not doing that anymore, this error is repeatedly coming up (even in a different environment!).

I'm really stuck because there's nothing I can really do... Changing DTC settings doesn't seem to help, and the error is originating from deep inside CSLA.NET data portal update code, not my custom code. Can anyone shed some light on this?

Thanks so much.

Mark

JonnyBee replied on Wednesday, February 23, 2011

The DTC gets involved when your transactions span more than one database connection,

Ie, your code uses 2 (or more) connections for update to the same database - or separate connections to different databases for updates.

If it is the same database the use SqlProfiler to see the actual connections and which updates/SQL are called.

Also, make sure your code uses the .ConnectionManager, ContextManager (L2S) , ObjectContextManager (EF) to keep the very same instance of DatabaseConnection during updates from a number of objects.

 

 

 

 

rmp251 replied on Wednesday, February 23, 2011

Thanks for the reply.

I've started a trace on audit login/logout and existing connections in SQL Server Profiler.  But a lot of information is coming in and I'm not sure exactly what I'm looking for.  There's only one .NET Client connection but there are a couple other connections because I'm querying the database in SQL Server Management Studio - but that has never been a problem before.

My code uses ContextManager, typically of this form:

            using (var ctx = ContextManager<AraDataContext>.GetManager("Ara"))

            {  ctx.DataContext.Update etc.  }

But I'm not using a ConnectionManager, never seemed to need it before.

Any more tips on diagnosing the problem? Thanks.

RockfordLhotka replied on Wednesday, February 23, 2011

This is discussed in the data access FAQ: http://www.lhotka.net/cslanet/faq/DataFaq.ashx

rmp251 replied on Tuesday, March 08, 2011

Thanks for the replies. I'm still at a loss for what to do about this, and it is causing very serious problems for our organization. The helpdesk is flooded, people aren't able to get there work done. Is there any workaround I can implement, such as catching the exception and forcing other db connections to close so there is only one connection?

Thanks,
Mark

 

rmp251 replied on Tuesday, March 08, 2011

The reason I'm having trouble with this is because the error is originating from within the CSLA framework (BusinessBase<T> class). The code I am using is very simple:

            if (!this.IsNew)
                return base.Save();

This is where the exception happens and I don't have any more code to step into.

Also, this only happens with a few seemingly random objects, and even in separate environments.  If I synchronize the databases for the different environments, and try the same action in both environments, the same error occurs, which makes me think there is something about the data itself that is causing the error. But the error seems to have to do with multiple database connections. I have no idea what is going on....

Thanks.

 

 

RockfordLhotka replied on Tuesday, March 08, 2011

I am pretty sure a couple people have answered - perhaps not clearly enough.

When using TransactionScope, if you open more than one connection to a database (even the same database, and not at the same time) within the transaction, TransactionScope will invoke the DTC.

By default the DTC isn't enabled on client workstations, and may not be properly configured on your servers. So the result is often an exception.

You can solve this two ways. Either enable and configure DTC on all machines, or avoid opening more than one database connection within a transaction.

The Csla.Data namespace includes several types (like ConnectionManager) designed to help you reuse an existing connection.

rmp251 replied on Tuesday, March 08, 2011

Thank you Rocky.  I admit my understanding is not quite up to par but I managed to dig a little deeper. I'm not sure about the difference between ContextManager and ConnectionManager but we are using ContextManager.  Can you see anything wrong with this code?

        [Transactional(TransactionalTypes.TransactionScope)]
        protected override void DataPortal_Update()
        {
            using (var ctx = ContextManager<AraDataContext>.GetManager("Ara"))
            {
                ctx.DataContext.UpdateBasicAra(.....); // ERROR (sometimes)
            }
        }

I did enable & configure DTC as well and the result was just a different error message:  "Communication with the underlying transaction manager has failed."

Thanks.

RockfordLhotka replied on Tuesday, March 08, 2011

ContextManager allows the reuse of LINQ to SQL context objects. Because a context object contains a connection object, the result is that you are reusing both the context and connection.

Your code is fine, assuming that at no point is there any other code using a context that might be invoked outside of that using block.

For example, your code can't actually be that simple right? You need to copy the data from the object into the L2S entity object(s) before doing the update. If there are child objects in your object graph, their data is copied into entity objects too.

ALL THAT MUST BE DONE WITHIN THE TOP-LEVEL using BLOCK.

These manager classes implement reference counting to keep the connection/context open and enable reuse. For this to work, the top-level using block can't exit until all child data access (all in their own using blocks) are complete.

If your code does follow that structure, then you may have some other issue - but if that's the case it is outside my experience.

rmp251 replied on Wednesday, March 09, 2011

Thanks again. I think I'm getting somewhere with this. In some cases there was an indirect call to DataPortal.Fetch happening within that using block.  When I eliminate that call the error goes away so that must have something to do with it.  But that call uses a DIFFERENT ContextManager (it is accessing a different database) so I still don't see what the problem is...

ajj3085 replied on Wednesday, March 09, 2011

Your call to the Fetch which is accessing a different database is causing two connections within a SINGLE transactionscope will always promote to DTC.

rmp251 replied on Thursday, March 10, 2011

Thanks guys. That explains it. Now the question is whether to eliminate the possibility of fetching within the transaction scope, or not using TransactionScope in the first place.

What is the purpose of using TransactionScope, and how necessary is it? Is it basically to protect data from being written to by two connections at the same time? What is the worst-case consequence of NOT using TransactionScope?

tmg4340 replied on Thursday, March 10, 2011

TransactionScope is a .NET construct that simplifies transaction management.  The rules are fairly simple - if all your transactional code happens on one resource, then the DTC doesn't get involved.  As soon as you create a second resource - even if that second resource is a database connection using the same connection string as an already-open one - and enlist that in your TransactionScope, then the transaction is "promoted", and the DTC gets involved.  It's these rules that spawned the various "manager" classes in CSLA; they help you re-use existing resources so you don't accidentally promote your transaction.

If you don't use it, then "the worst that happens" is that you have to manage your transactions on your own.  Nothing in CSLA prevents you from spinning up your own Transaction objects - you just have to tell CSLA that you're doing that by using the [Transactional(TransactionTypes.Manual)] decoration, and pass them around as you need them.  Or you can eschew transactions altogether (if you don't need them) and not use the Transactional attribute at all.

It's not uncommon for people to not use the Transactional attribute on their Fetch methods, but use it for the Update/Delete methods.  Rarely do you need to get data in a transactional context.

HTH

- Scott

ajj3085 replied on Thursday, March 10, 2011

Scotts right.  You likely don't need your fetch to be done in a transaction at all.  Of course you'll be enlisted automatically.  So set the fetch's transactional attribute to manual, new up your own transactionscope and specifiy Supress as the transactionscopeoption.  Basically that's telling TS that you don't want the fetch in any transaction.

If you DO want the fetch in a transaction, you can use RequiresNew instead. I believe that should create a second, separate transaction for your fetch. 

rmp251 replied on Thursday, March 10, 2011

Actually, the Fetch is already NOT marked with a transaction. But the Update is, and the Fetch is happening inside the update. And as YOU said:

"Your call to the Fetch which is accessing a different database is causing two connections within a SINGLE transactionscope will always promote to DTC."

 

tmg4340 replied on Thursday, March 10, 2011

And as we've said, since the Fetch is called within the Update method, it will automatically be enlisted in the TransactionScope, because the TransactionScope is live when the Fetch is called.  .NET database connections will automatically enlist in a live TransactionScope unless you tell them not to.

You need to follow Andy's advice - mark your Fetch method with the [Transactional(TransactionTypes.Manual)] attribute, and manually create a new TransactionScope with the Suppress TransactionScopeOption.  Then put your Fetch DB code inside your created TransactionScope.  That should exclude it from the existing TransactionScope surrounding the Update, and you won't be creating a distributed transaction.

HTH

- Scott

rmp251 replied on Wednesday, March 16, 2011

Thanks Scott, that seems to work.

Copyright (c) Marimer LLC