2 different transactions in a UBAQ BPM

I have a BPM on a UBAQ that I am basically using as a function. The first part of the code moves inventory from one PCID to another. That part works fine. After some testing, we realized that we also need to handle any material queue requests that were on the original PCID and create the same requests for the new PCID, (as well as remove requests from PCID’s that are subsequently empty, but I’m not there yet). When I add the code to create the move request, I get a server error that says that it can’t complete the request becuase the session is not single threaded. It does make/finish the build/split/merge move to the new PCID, but it doesn’t create the move request.

If I wrap the whole thing in a transcope it fails on the create move request because the PCID doesn’t have anything in it yet. Then rolls is all back.

Any idea on what I need to do to get this working? Do I need to wrap each transaction in it’s own scope? Or use 2 different code blocks?

Server Error
Query failed to run
System.Data.Entity.Core.EntityException: The underlying provider failed on EnlistTransaction. ---> System.Data.SqlClient.SqlException: The operation failed because the session is not single threaded.
   at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
   at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
   at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
   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.PropagateTransactionCookie(Byte[] cookie)
   at System.Data.SqlClient.SqlInternalConnection.EnlistNonNull(Transaction tx)
   at System.Data.SqlClient.SqlInternalConnection.Enlist(Transaction tx)
   at System.Data.SqlClient.SqlInternalConnection.EnlistTransaction(Transaction transaction)
   at System.Data.SqlClient.SqlConnection.EnlistTransaction(Transaction transaction)
   at Epicor.Data.Provider.EpiConnection.EnlistTransaction(Transaction transaction) in C:\_Releases\ICE\UD10.2.500.27FW\Source\Framework\Epicor.System\Data\EpiProvider2\EpiConnection.cs:line 208
   at System.Data.Entity.Infrastructure.Interception.InternalDispatcher`1.Dispatch[TTarget,TInterceptionContext](TTarget target, Action`2 operation, TInterceptionContext interceptionContext, Action`3 executing, Action`3 executed)
   at System.Data.Entity.Infrastructure.Interception.DbConnectionDispatcher.EnlistTransaction(DbConnection connection, EnlistTransactionInterceptionContext interceptionContext)
   at System.Data.Entity.Core.EntityClient.EntityConnection.EnlistTransaction(Transaction transaction)
   --- End of inner exception stack trace ---
   at System.Data.Entity.Core.EntityClient.EntityConnection.EnlistTransaction(Transaction transaction)
   at System.Data.Entity.Core.Objects.ObjectContext.EnsureContextIsEnlistedInCurrentTransaction[T](Transaction currentTransaction, Func`1 openConnection, T defaultValue)
   at System.Data.Entity.Core.Objects.ObjectContext.EnsureConnection(Boolean shouldMonitorTransactions)
   at System.Data.Entity.Core.Objects.ObjectContext.ExecuteInTransaction[T](Func`1 func, IDbExecutionStrategy executionStrategy, Boolean startLocalTransaction, Boolean releaseConnectionOnSuccess)
   at System.Data.Entity.Core.Objects.ObjectQuery`1.<>c__DisplayClass7.<GetResults>b__5()
   at System.Data.Entity.Core.Objects.ObjectQuery`1.GetResults(Nullable`1 forMergeOption)
   at System.Data.Entity.Core.Objects.ObjectQuery`1.<System.Collections.Generic.IEnumerable<T>.GetEnumerator>b__0()
   at System.Data.Entity.Internal.LazyEnumerator`1.MoveNext()
   at System.Linq.Enumerable.FirstOrDefault[TSource](IEnumerable`1 source)
   at System.Data.Entity.Core.Objects.CompiledQuery.ExecuteQuery[TResult](ObjectContext context, Object[] parameterValues)
   at System.Data.Entity.Core.Objects.CompiledQuery.Invoke[TArg0,TArg1,TArg2,TResult](TArg0 arg0, TArg1 arg1, TArg2 arg2)
   at Epicor.Data.DBExpressionCompiler.GetResult[TDataContext,TQuery,TResult](Func`3 executeQuery, Cache cacheSetting, TDataContext dataContext, TQuery query) in C:\_Releases\ICE\UD10.2.500.27FW\Source\Framework\Epicor.System\Data\DBExpressionCompiler.cs:line 445
   at Epicor.Data.DBExpressionCompiler.InvokeSingle[TDataContext,TQuery,TResult](Expression expression, Cache currentCacheSetting, Boolean cacheQuery, TDataContext dataContext, Func`2 getDataCacheKey, Func`2 compileQuery, Func`3 executeQuery) in C:\_Releases\ICE\UD10.2.500.27FW\Source\Framework\Epicor.System\Data\DBExpressionCompiler.cs:line 301
(ctx, company_ex, empID_ex) => ctx.EmpBasic.Where(row => ((row.Company == company_ex) AndAlso (row.EmpID == empID_ex))).Select(row => row.Name).FirstOrDefault()
BPM code
Action<string> WT = (msg) =>
{
    Epicor.Hosting.Trace.ServerLog.WriteTraceMessage(
    "trace://ice/Brandon/PCIDProject",
    "PCIDMovePartsBAQ",
    () => msg
    );
};


var x = ttResults.FirstOrDefault();
{

        using (Erp.Contracts.PkgControlIDBuildSplitMergeSvcContract pcidMove = Ice.Assemblies.ServiceRenderer.GetService<Erp.Contracts.PkgControlIDBuildSplitMergeSvcContract>(Db))
        {
            Erp.Tablesets.PCIDBuildSplitMergeSourceTableset sourceDS = new Erp.Tablesets.PCIDBuildSplitMergeSourceTableset();
            Erp.Tablesets.PCIDBuildSplitMergeDestTableset destDS = new Erp.Tablesets.PCIDBuildSplitMergeDestTableset();
            pcidMove.GetSourcePCID(x.Calculated_SourcePCID, true, ref sourceDS);
            WT("1: pcidMove.GetSourcePCID(x.Calculated_SourcePCID,true, ref sourceDS);");
            bool partexists = false;
            bool lotExists = false;
            foreach (var row in sourceDS.PCIDBuildSplitMergeSourceItem.Where(y => y.ItemPartNum == x.Calculated_PartNum))
            {
                partexists = true;
                if (row.ItemLotNum == x.Calculated_LotNum)
                {
                    row.SelectedForAction = true;
                    row.MoveQuantity = x.Calculated_MoveQty;
                    row.RowMod = IceRow.ROWSTATE_UPDATED;
                    lotExists = true;
                }
            }

            pcidMove.GetDestPCID(x.Calculated_DestPCID, true, ref destDS);
            WT("2: pcidMove.GetDestPCID(x.Calculated_DestPCID, true, ref destDS);");
            if (destDS.PCIDBuildSplitMergeDest[0].BinNum == null || destDS.PCIDBuildSplitMergeDest[0].BinNum == string.Empty)
            {
                destDS.PCIDBuildSplitMergeDest[0].BinNum = sourceDS.PCIDBuildSplitMergeSource[0].BinNum;
            }
            string pcidNotMovedMessage;
            string serialTrackPartsNotMovedMessage;
            pcidMove.MoveFromSourcePCIDToDestPCID(ref sourceDS, ref destDS, false, false, out pcidNotMovedMessage, out serialTrackPartsNotMovedMessage);

            WT("3: pcidMove.MoveFromSourcePCIDToDestPCID(ref sourceDS, ref destDS, false, false, out pcidNotMovedMessage, out serialTrackPartsNotMovedMessage );");
            if (pcidNotMovedMessage != null && pcidNotMovedMessage != string.Empty)
                x.Calculated_ErrorOut = "testMessage";
            if (partexists == false)
            {
                x.Calculated_ErrorOut = "Part Doesn't exist in Source PCID";
                WT(x.Calculated_ErrorOut);
            }
            else if (lotExists == false)
            {
                x.Calculated_ErrorOut = "Specified lot does not exist in the source PCID";
                WT(x.Calculated_ErrorOut);
            }
            else
            {
                x.Calculated_ErrorOut = "Move Successful";
                WT(x.Calculated_ErrorOut);
                //this is to handle the material queue moves.
                var mtlQSeq = (from m in Db.MtlQueue
                               where m.Company == Session.CompanyID
                               && m.PCID == x.Calculated_SourcePCID
                               select m.MtlQueueSeq);
                foreach (var ms in mtlQSeq)
                {

                    Erp.Contracts.MaterialQueueSvcContract mtlQRecord = Ice.Assemblies.ServiceRenderer.GetService<Erp.Contracts.MaterialQueueSvcContract>(Db);
                    {
                        Erp.Tablesets.MaterialQueueTableset mqds = new Erp.Tablesets.MaterialQueueTableset();
                        mqds = mtlQRecord.GetByID(ms);
                        WT("4: mtlQRecord.GetByID(ms);");
                        Erp.Contracts.MoveRequestSvcContract moveRec = Ice.Assemblies.ServiceRenderer.GetService<Erp.Contracts.MoveRequestSvcContract>(Db);
                        {
                            Erp.Tablesets.MoveRequestTableset mrds = new Erp.Tablesets.MoveRequestTableset();
                            moveRec.GetNewMoveRequest("MI", ref mrds);
                            WT("5:moveRec.GetNewMoveRequest(\"MI\", ref mrds);");
                            string warehouseList;
                            moveRec.OnChangePCID(x.Calculated_DestPCID, ref mrds, out warehouseList);
                            WT("6: moveRec.OnChangePCID");
                            moveRec.OnChangeToWhse(mqds.MtlQueue[0].ToWhse, ref mrds);
                            WT("7: moveRec.OnChangeToWhse");
                            moveRec.OnChangeToBin(mqds.MtlQueue[0].ToBinNum, ref mrds);
                            WT("8: moveRec.OnChangeToBin");
                            moveRec.ProcessRequest(ref mrds);
                            WT("9: moveRec.ProcessRequest");

                        }

                    }

            }

        }
    }

}

Try changing the line that reads like this

                //this is to handle the material queue moves.
                var mtlQSeq = (from m in Db.MtlQueue
                               where m.Company == Session.CompanyID
                               && m.PCID == x.Calculated_SourcePCID
                               select m.MtlQueueSeq);

to

                //this is to handle the material queue moves.
                var mtlQSeq = (from m in Db.MtlQueue.With(LockHint.NoLock)
                               where m.Company == Session.CompanyID
                               && m.PCID == x.Calculated_SourcePCID
                               select m.MtlQueueSeq).ToList();

I’m not sure it will resolve your issue but that’s what I would try first. Hope that helps

1 Like

@Banderson @Chris_Conn and I ran into this and he made three separate bpms stuffed the data into a dataset and then after the transaction was completed he made a new scope to read the dataset and write to the table.

Good Luck

Greg

1 Like