This project is read-only.

IMembership.CreateUser Nhibernate issue

Topics: Customizing Orchard, Troubleshooting, Writing modules
Jun 29, 2012 at 10:19 AM
Edited Jun 29, 2012 at 10:20 AM

Hi There,

I have been working with Orchard for the last 2 weeks now. I have come across an issue that I am not sure how to solve as I don't know enough about the data access yet.

Anyway I have built a module that overrides the built in User Module as I am using the authentication of another system that we are integrating into orchard.

What I am doing is the overriding the MembershipService. In the ValidateUser method I check the credentials against another system. If the credentials pass I then check to see if the user exists in Orchard. If it doesn't I am trying to create a user using IMembership.CreateUser. The weird thing is it returns an IUser object but the data is not written to the database. After continuing the code orchard just crashes and gives the following error in the logs


2012-06-29 21:08:00,143 [11] NHibernate.Event.Default.AbstractFlushingEventListener - Could not synchronize database state with session
NHibernate.StaleStateException: Unexpected row count: -1; expected: 1
   at NHibernate.AdoNet.Expectations.BasicExpectation.VerifyOutcomeNonBatched(Int32 rowCount, IDbCommand statement)
   at NHibernate.AdoNet.NonBatchingBatcher.AddToBatch(IExpectation expectation)
   at NHibernate.Persister.Entity.AbstractEntityPersister.Insert(Object id, Object[] fields, Boolean[] notNull, Int32 j, SqlCommandInfo sql, Object obj, ISessionImplementor session)
   at NHibernate.Persister.Entity.AbstractEntityPersister.Insert(Object id, Object[] fields, Object obj, ISessionImplementor session)
   at NHibernate.Action.EntityInsertAction.Execute()
   at NHibernate.Engine.ActionQueue.Execute(IExecutable executable)
   at NHibernate.Engine.ActionQueue.ExecuteActions(IList list)
   at NHibernate.Engine.ActionQueue.ExecuteActions()
   at NHibernate.Event.Default.AbstractFlushingEventListener.PerformExecutions(IEventSource session)
2012-06-29 21:08:00,190 [11] NHibernate.Impl.AbstractSessionImpl - DTC transaction prepre phase failed
System.Transactions.TransactionException: The operation is not valid for the state of the transaction. ---> System.Data.SqlClient.SqlException: Timeout expired.  The timeout period elapsed prior to completion of the operation or the server is not responding.
   at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection)
   at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning()
   at System.Data.SqlClient.TdsParserStateObject.ReadSniError(TdsParserStateObject stateObj, UInt32 error)
   at System.Data.SqlClient.TdsParserStateObject.ReadSni(DbAsyncResult asyncResult, TdsParserStateObject stateObj)
   at System.Data.SqlClient.TdsParserStateObject.ReadNetworkPacket()
   at System.Data.SqlClient.TdsParserStateObject.ReadBuffer()
   at System.Data.SqlClient.TdsParserStateObject.ReadByte()
   at System.Data.SqlClient.TdsParser.Run(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj)
   at System.Data.SqlClient.TdsParser.TdsExecuteTransactionManagerRequest(Byte[] buffer, TransactionManagerRequestType request, String transactionName, TransactionManagerIsolationLevel isoLevel, Int32 timeout, SqlInternalTransaction transaction, TdsParserStateObject stateObj, Boolean isDelegateControlRequest)
   at System.Data.SqlClient.SqlInternalConnectionTds.ExecuteTransactionYukon(TransactionRequest transactionRequest, String transactionName, IsolationLevel iso, SqlInternalTransaction internalTransaction, Boolean isDelegateControlRequest)
   at System.Data.SqlClient.SqlInternalConnectionTds.ExecuteTransaction(TransactionRequest transactionRequest, String name, IsolationLevel iso, SqlInternalTransaction internalTransaction, Boolean isDelegateControlRequest)
   at System.Data.SqlClient.SqlDelegatedTransaction.SinglePhaseCommit(SinglePhaseEnlistment enlistment)
   --- End of inner exception stack trace ---
   at System.Transactions.TransactionState.Rollback(InternalTransaction tx, Exception e)
   at System.Transactions.Transaction.Rollback()
   at System.Transactions.TransactionScope.InternalDispose()
   at System.Transactions.TransactionScope.Dispose()
   at NHibernate.Transaction.AdoNetWithDistrubtedTransactionFactory.DistributedTransactionContext.System.Transactions.IEnlistmentNotification.Prepare(PreparingEnlistment preparingEnlistment)


I haven't really worked with NHibernate before so I am not sure why this is happening and hope someone can point me in the right direction. Below is a snippet of the code:


 public IUser ValidateUser(string userNameOrEmail, string password)
            var lowerName = userNameOrEmail == null ? "" : userNameOrEmail.ToLowerInvariant();

            //First Validate the user in the Rev System
            string instanceExternalId = _shellSettings.DataTablePrefix;

            //Get the instance based on the datatable prefix (ExternalId)
            var instance = _instanceEngine.GetInstanceFromExternalId(instanceExternalId);

            BI.Rev.Core.DataContracts.User revUser = null;

            revUser = _usersEngine.GetByUsername(userNameOrEmail);

            string token = null;

            if (revUser != null)
                token = SSOManager.LoginUser(userNameOrEmail, password);
            bool hasAccess = false;

            if (token != null)
                revUser.Instances = _usersEngine.GetUserInstances(revUser.UserId);

                if (instance != null)
                    hasAccess = revUser.Instances.Where(i => i.InstanceId == instance.InstanceId).Count() > 0;

                if (_usersEngine.IsInSystemRole(revUser.UserId))

                    hasAccess = true;

            if (hasAccess)
                WebSecurityManager.LogonUser(revUser, token);
            IUser user = null;

            if (revUser !=null && hasAccess)
                //If the user validates then try and find the user in orchard
                user = _orchardServices.ContentManager.Query<UserPart, UserPartRecord>().Where(u => u.NormalizedUserName == lowerName).List().FirstOrDefault();

                if (user == null)
                    //if the user does not exist in Orchard then create one.
                    CreateUserParams userParams = new CreateUserParams(revUser.Username, revUser.Password, revUser.Email, revUser.SecretQuestion, revUser.SecretAnswer, true);

                    user = CreateUser(userParams); 

            return user;
Jun 29, 2012 at 6:24 PM

You can call _contentManager.Flush() after creating the user.

Jun 29, 2012 at 8:46 PM

Thank you sabstienros!!! You just made my day :) That did the trick. Does the Flush method commit the transaction or something?

Jun 29, 2012 at 8:53 PM

It commits the local transaction, and because the default isolaation level is ReadCommitted it's needed if in the same request you intend to read some data that has just been created.