Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Enlisting System.Web.Providers in a TransactionScope

We are trying to integrate the System.Web.Providers membership management into a transaction using System.Transactions.TransactionScope and keep getting the following error message:

The operation is not valid for the state of the transaction.

Wrapped up in an exception with the following, more confusing message:

The provider did not return a ProviderManifestToken string.

Before anyone asks, yes we have verified that the connection string is correct and the user we're using to connect to the db has the proper permissions. If we take the attempt to call Membership.CreateUser() out of a TransactionScope block, it works. Put it in a TransactionScope and it fails.

There seems to be amazingly little information out there about the Universal Provider. Pretty much all you see are links to the NuGet page and Scott Hanselman's blog post from June.

Does anyone out there know how to get System.Web.Providers to participate in a transaction?

Thanks.

Here's the full stacktrace, in case it's helpful:

System.Data.ProviderIncompatibleException was unhandled by user code
  Message=The provider did not return a ProviderManifestToken string.
  Source=System.Data.Entity
  StackTrace:
       at System.Data.Common.DbProviderServices.GetProviderManifestToken(DbConnection connection)
       at System.Web.Providers.Entities.ModelHelper.GetStorageMetadata(String providerName, DbConnection connection, String ssdl)
       at System.Web.Providers.Entities.ModelHelper.CreateMetadataWorkspace(String providerName, DbConnection connection, String csdl, String ssdl, String msl)
       at System.Web.Providers.Entities.ModelHelper.CreateEntityConnection(ConnectionStringSettings setting, String csdl, String ssdl, String msl)
       at System.Web.Providers.Entities.ModelHelper.CreateMembershipEntities(ConnectionStringSettings setting)
       at System.Web.Providers.DefaultMembershipProvider.Membership_CreateUser(String applicationName, String userName, String password, String salt, String email, String passwordQuestion, String passwordAnswer, Boolean isApproved, DateTime& createDate, Boolean uniqueEmail, Int32 passwordFormat, Object& providerUserKey)
       at System.Web.Providers.DefaultMembershipProvider.CreateUser(String username, String password, String email, String passwordQuestion, String passwordAnswer, Boolean isApproved, Object providerUserKey, MembershipCreateStatus& status)
       at System.Web.Security.Membership.CreateUser(String username, String password, String email, String passwordQuestion, String passwordAnswer, Boolean isApproved, Object providerUserKey, MembershipCreateStatus& status)
       at WcfLoginRegister.RegistrationService.RegisterUser(RegisterUserRequest request) in C:\Users\rmacgrogan\dev\pallas\parthenon\sandbox\FbEntityTypeTester\WcfLoginRegister\RegistrationService.svc.cs:line 43
       at SyncInvokeRegisterUser(Object , Object[] , Object[] )
       at System.ServiceModel.Dispatcher.SyncMethodInvoker.Invoke(Object instance, Object[] inputs, Object[]& outputs)
       at     System.ServiceModel.Dispatcher.DispatchOperationRuntime.InvokeBegin(MessageRpc& rpc)
  InnerException: System.Transactions.TransactionException
       Message=The operation is not valid for the state of the transaction.
       Source=System.Transactions
       StackTrace:
            at     System.Transactions.TransactionState.EnlistPromotableSinglePhase(InternalTransaction tx, IPromotableSinglePhaseNotification promotableSinglePhaseNotification, Transaction atomicTransaction)
            at System.Transactions.Transaction.EnlistPromotableSinglePhase(IPromotableSinglePhaseNotification promotableSinglePhaseNotification)
            at System.Data.SqlClient.SqlInternalConnection.EnlistNonNull(Transaction tx)
            at System.Data.SqlClient.SqlInternalConnection.Enlist(Transaction tx)
            at System.Data.SqlClient.SqlInternalConnectionTds.Activate(Transaction transaction)
            at System.Data.ProviderBase.DbConnectionInternal.ActivateConnection(Transaction transaction)
            at System.Data.ProviderBase.DbConnectionPool.GetConnection(DbConnection owningObject)
            at System.Data.ProviderBase.DbConnectionFactory.GetConnection(DbConnection owningConnection)
            at System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory)
            at System.Data.SqlClient.SqlConnection.Open()
            at System.Data.SqlClient.SqlProviderServices.UsingConnection(SqlConnection sqlConnection, Action`1 act)
            at System.Data.SqlClient.SqlProviderServices.UsingMasterConnection(SqlConnection sqlConnection, Action`1 act)
            at System.Data.SqlClient.SqlProviderServices.GetDbProviderManifestToken(DbConnection connection)
            at System.Data.Common.DbProviderServices.GetProviderManifestToken(DbConnection connection)
   InnerException: 

Update

I did a simplified test to get better access to the real underlying error, and Bahri Gungor is correct that the underlying issue is that MSDTC is not enabled.

However, since I am trying to run this code in an Azure project and since Azure does not support distributed transactions, I am stuck using an ugly workaround. All membership writes must occur in a separate transaction, which is pretty unfortunate.

Thanks everyone for your help.

like image 476
Robert MacGrogan Avatar asked Oct 24 '11 20:10

Robert MacGrogan


People also ask

What is Msdtc service?

The Microsoft Distributed Transaction Coordinator (MSDTC) service is a component of modern versions of Microsoft Windows that is responsible for coordinating transactions that span multiple resource managers, such as databases, message queues, and file systems.

What is an ambient transaction?

The ambient transaction is the transaction within which your code executes. You can obtain a reference to the ambient transaction by calling the static Transaction. Current property of the Transaction class.

What is a lightweight transaction in Ado net?

A promotable transaction is a lightweight (local) transaction that can be automatically promoted to a fully distributed transaction on an as-needed basis. Starting with ADO.NET 2.0, System. Data. SqlClient supports promotable transactions when you work with SQL Server.

What is distributed transactions in C#?

Distributed Transaction- Where application deals with various databases and perform all data manipulation action on multiple database. To apply Distributed Transaction we have to start DTC (Distributed Transaction Coordinator) service from Services panel. Note- An application can have both types of Transactions.


2 Answers

This can be caused when you have nested TransactionScopes:

        using (var ts = new TransactionScope())
        {
            using(var tsInner1 = new TransactionScope())
            {
                //OOPS, I forgot to call Complete() or Rollback()
            }
            using (var tsInner2 = new TransactionScope())
            {
                //Any db action followed by a "Complete" will cause this error
                tsInner2.Complete();
            }
        }

But I cannot be sure without seeing the code that generated the error.

like image 31
CD Waddell Avatar answered Sep 19 '22 12:09

CD Waddell


I haven't tried using TransactionScope with ASPNet Membership Services, however, anything that will open another transaction (even a second connection to the database) will automatically initialize an MSDTC transaction. If MSDTC is not configured on the machine (I assume your web server), the transaction cannot be created successfully.

Under Administrative Tools, there is a management tool called Component Services (Control Panel -> System and Security -> Administrative Tools). Once the tool opens, you will see Component Services on the left hand side. Open Component Services -> Computers -> My Computer -> Distributed Transaction Coordinator -> Local DTC (right click and select Properties). Select the Security Tab.

Make sure Transaction Manager Communication has both Allow Inbound and Allow Outbound checked. Network DTC access should also be checked. I can't be any more specific about the other settings because your specific environment might require different settings for the other variables.

I believe what is happening is that your access of the ASPNet Membership Services (which have its own SQL Server configuration) is promoting the transaction to a MSDTC transaction, and some setting (security or configuration of MSDTC) is preventing it from succeeding. You will find all sorts of MSDTC troubleshooting literature with a simple Google search (many of which are resolved on Stack Overflow). However, I would check your Component Services configuration first, and make sure you can execute a distributed transaction.

like image 62
Bahri Gungor Avatar answered Sep 18 '22 12:09

Bahri Gungor