Transactions and multiple DataAccessAdapters

Posts   
 
    
Posts: 30
Joined: 17-Sep-2006
# Posted on: 19-Sep-2007 18:11:05   

Hi everyone,

We're using a TransactionScope object in .NET 2.0 to perform our transactions. We're using a

using(DataAccessAdapter da = new DataAccessAdapter()) { // do stuff }

type pattern in our business logic. However, the function in which the transaction takes place calls a number of business logic methods, each of which instantiates their own DataAccessAdapter (and then goes out of scope again). This is resulting in the following exception. It does not occur when we just use one DataAccessAdapter for the entire transaction. Any suggestions?

Thanks!


The transaction manager has disabled its support for remote/network transactions.

[COMException (0x8004d024): The transaction manager has disabled its support for remote/network transactions. (Exception from HRESULT: 0x8004D024)] System.Transactions.Oletx.IDtcProxyShimFactory.ReceiveTransaction(UInt32 propgationTokenSize, Byte[] propgationToken, IntPtr managedIdentifier, Guid& transactionIdentifier, OletxTransactionIsolationLevel& isolationLevel, ITransactionShim& transactionShim) +0 System.Transactions.TransactionInterop.GetOletxTransactionFromTransmitterPropigationToken(Byte[] propagationToken) +218

[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.Transactions.Oletx.OletxTransactionManager.ProxyException(COMException comException) +327 System.Transactions.TransactionInterop.GetOletxTransactionFromTransmitterPropigationToken(Byte[] propagationToken) +307 System.Transactions.TransactionStatePSPEOperation.PSPEPromote(InternalTransaction tx) +70 System.Transactions.TransactionStateDelegatedBase.EnterState(InternalTransaction tx) +201 System.Transactions.EnlistableStates.Promote(InternalTransaction tx) +12 System.Transactions.Transaction.Promote() +53 System.Transactions.TransactionInterop.ConvertToOletxTransaction(Transaction transaction) +28 System.Transactions.TransactionInterop.GetExportCookie(Transaction transaction, Byte[] whereabouts) +202 System.Data.SqlClient.SqlInternalConnection.EnlistNonNull(Transaction tx) +527 System.Data.SqlClient.SqlInternalConnection.Enlist(Transaction tx) +740108 System.Data.SqlClient.SqlInternalConnectionTds.Activate(Transaction transaction) +734295 System.Data.ProviderBase.DbConnectionInternal.ActivateConnection(Transaction transaction) +30 System.Data.ProviderBase.DbConnectionPool.GetConnection(DbConnection owningObject) +1209 System.Data.ProviderBase.DbConnectionFactory.GetConnection(DbConnection owningConnection) +82 System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory) +105 System.Data.SqlClient.SqlConnection.Open() +111 SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.OpenConnection() +188 SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.StartTransaction(IsolationLevel isolationLevelToUse, String name) +265 SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.SaveEntityCollection(IEntityCollection2 collectionToSave, Boolean refetchSavedEntitiesAfterSave, Boolean recurse) +930 SD.LLBLGen.Pro.ORMSupportClasses.DataAccessAdapterBase.SaveEntityCollection(IEntityCollection2 collectionToSave) +36

Walaa avatar
Walaa
Support Team
Posts: 14995
Joined: 21-Aug-2005
# Posted on: 19-Sep-2007 18:26:52   

the function in which the transaction takes place calls a number of business logic methods, each of which instantiates their own DataAccessAdapter (and then goes out of scope again). This is resulting in the following exception. It does not occur when we just use one DataAccessAdapter for the entire transaction. Any suggestions?

Would you please post a complete code snippet?

Posts: 30
Joined: 17-Sep-2006
# Posted on: 19-Sep-2007 19:12:16   

Certainly!


public UserCreateStatus RegisterUser(UserEntity user)
        {
            user.RoleId = UserRole.StandardUser;
            user.AccountStatus = UserAccountStatus.AccountPending;

            using (TransactionScope ts = new TransactionScope())
            {
                using (DataAccessAdapter a = new DataAccessAdapter())
                {
                    bool result = a.SaveEntity(user, true);

                    if (result)
                    {
                        EmailProvider.Instance.SendRegistrationEmail(user, activationHash);
                        // now try to associate any temporary bricks with this account
                        TemporaryBrickManager.Instance.AssociateTemporaryBricksWithAccount(user.UserId);

                        ts.Complete();
                        return UserCreateStatus.Success;
                    }
                    else
                    {
                        return UserCreateStatus.UnknownFailure;
                    }
                }

            }
        }

and in AssociateTemporaryBricksWithAccount

public bool AssociateTemporaryBricksWithAccount(int userId)
        {
            List<Guid> guids = GetTemporaryBrickIDs();
            if (guids.Count == 0)
                return false;
            using (DataAccessAdapter da = new DataAccessAdapter())
            {
                ....
                da.SaveEntityCollection(temporaryBricks);
            }
            return true;
        }

Posts: 30
Joined: 17-Sep-2006
# Posted on: 19-Sep-2007 19:13:20   

The transaction manager MsDTC isn't enabled for network transactions - but just trying to understand why it would need to be, when it works fine when only one DataAccessAdapter is used. I'm guessing this might have somethign to do with spanning multiple connections or something?

Faldaani
User
Posts: 14
Joined: 18-Sep-2007
# Posted on: 19-Sep-2007 23:36:03   

By no means an expert on LLBLGen, but opening more than one connection at the same time while using the System.Transactions will result in a promoted transaction, even it uses the exact same connection string. Promoted transactions (DTC) are pure evil, unless you really really need them.

This does not sound like one of those times.

I made a class that wraps and instantiates the data adapter, opens a connection and starts a transaction when wrapped in a using statement, much like you do with the adapter. Then I pass that object to every data layer method. If that data layer method throws an exception I catch it in the same method and set my object to "unhappy".

Unhappy means that when it reaches the end of the using() statement it will automatically rollback. If its happy it will automatically commit. Obviously I can do it explicitly too. This avoids multiple data adapters for me at least, but I'm sure that there are FAR better ways of doing it, although its very easy to test transactions with unit tests this way.

I've modified the Manager templates accept my object instead of DataAdapters, so it was quite easy to implement. Also made some internal business logic methods that accept the same object for those I need to call methods within the business layer to avoid code repeat.

If you're interested in my rather awkward way of doing it you can get back to me =)

EDIT: I can't spell.