StrataFrame Forum

Transactions Error: DEF_TRANS_KEY has not been started

http://forum.strataframe.net/Topic17105.aspx

By Bill Cunnien - 6/16/2008

Here is my code:

private void UpdateAspireForTransfer()
{
    try
    {
       
BusinessLayer.TransactionBegin("Aspire", IsolationLevel.ReadCommitted);
       
// Retrieve the next Batch ID
       
String mBatchID = GetNextBatchID();
        AssignBatchID(mBatchID);
        CreateCommissions(mBatchID);
       
BusinessLayer.TransactionCommit("Aspire");
        UpdateDynamicsForTransfer(mBatchID);
    }
    catch
    {
       
BusinessLayer.TransactionRollback("Aspire");
       
throw;
    }
}

For some reason, yet unknown to me, the TransactionRollBack gets called and generates an error:

A transaction with the key [DEF_TRANS_KEY] has not been started.

and this stack trace:

at MicroFour.StrataFrame.Data.DbDataSourceItem.TransactionEnd(String TransactionKey, Boolean IsCommit)
at MicroFour.StrataFrame.Data.DbDataSourceItem.TransactionRollback(String TransactionKey)
at MicroFour.StrataFrame.Business.BusinessLayer.TransactionRollback(String DataSourceKey)
at Aspire.Accounting.Invoicing.UpdateAspireForTransfer()
at Aspire.Accounting.Invoicing.TransferTSB_Click(Object sender, EventArgs e)
at System.Windows.Forms.ToolStripItem.RaiseEvent(Object key, EventArgs e)
at System.Windows.Forms.ToolStripButton.OnClick(EventArgs e)
at System.Windows.Forms.ToolStripItem.HandleClick(EventArgs e)
at System.Windows.Forms.ToolStripItem.HandleMouseUp(MouseEventArgs e)
at System.Windows.Forms.ToolStripItem.FireEventInteractive(EventArgs e, ToolStripItemEventType met)
at System.Windows.Forms.ToolStripItem.FireEvent(EventArgs e, ToolStripItemEventType met)
at System.Windows.Forms.ToolStrip.OnMouseUp(MouseEventArgs mea)
at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.ScrollableControl.WndProc(Message& m)
at System.Windows.Forms.ToolStrip.WndProc(Message& m)
at System.Windows.Forms.Control.ControlNativewindow.OnMessage(Message& m)
at System.Windows.Forms.Control.ControlNativewindow.WndProc(Message& m)
at System.Windows.Forms.Nativewindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)

So, what am I doing wrong?

Thanks, for straightening me out,
Bill

By Bill Cunnien - 6/16/2008

Does this additional code help?

private void AssignBatchID(String pBatchID)
{
   
// attach the batch number to only the selected invoice records
   
for (int i = 0; i < invselection.SelectedCount; i++)
    {
       
InvoicesCurrentBO mCurrInvoice = (InvoicesCurrentBO)invselection.GetSelectedRow(i);
       
InvoiceMasterBO mInvMasterBO = new InvoiceMasterBO();
        mInvMasterBO.FillByPrimaryKey(mCurrInvoice.invoiceindex);
       
if (mInvMasterBO.Count > 0)
        {
            mInvMasterBO.Edit();
            mInvMasterBO.batchid = pBatchID;
            mInvMasterBO.Save();
        }
    }
}

I am thinking that the Save() method needs to reflect transactions, like this: mInvMasterBO.Save(true).

If I replace that method parameter whereever a save occurs inside that transaction, will that fix the error?

Bill

By Bill Cunnien - 6/16/2008

I changed all of the save methods to include the true parameter.  Now, I am getting a timeout error.  Does the ReadCommitted IsolationLevel place a lock on any records that were updated, essentially blocking further calls to that record?  Is there another IsolationLevel that I should be using?
By StrataFrame Team - 6/17/2008

Here's a good article describing the different isolation levels that are available for you to use:  http://msdn.microsoft.com/en-us/library/ms173763.aspx

Yes, using ReadCommitted can cause locking issues if you try to read the record again after you have updated it, but before you have committed the transaction.

As for why an exception is being thrown causing the rollback to occur, it is most likely that you need to change your Save() calls on your busines object to call Save(True, "Aspire") so that the business objects are saved on the transaction that you have started.  If you just call Save(True), the business objects will be saved on the "default" transaction, not the "Aspire" transaction.

By Bill Cunnien - 6/17/2008

Thanks, Ben!

ReadCommitted is definitely the safer approach.  I will try to stick with that and work around my transaction issues another way.

Bill

By Bill Cunnien - 6/17/2008

Still not working.

A transaction with the key [DEF_TRANS_KEY] has not been started.

Here is my code:

private void UpdateAspireForTransfer()
{
   
try
   
{
       
// Retrieve the next Batch ID
       
String mBatchID = GetNextBatchID();
        AssignBatchID(mBatchID);
       
BusinessLayer.TransactionBegin(invoicesCurrentBO1.DataSourceKey, IsolationLevel.ReadCommitted);
        CreateCommissions(mBatchID);
       
BusinessLayer.TransactionCommit(invoicesCurrentBO1.DataSourceKey);
        UpdateDynamicsForTransfer(mBatchID);
    }
   
catch
   
{
       
BusinessLayer.TransactionRollback(invoicesCurrentBO1.DataSourceKey);
       
throw;
    }
}

The AssignBatchID method assigns a batch ID to the selected invoices.  This was included in the transaction, but I pulled it out and am performing some checks to make sure I am not adding an invalid batch ID.  Then, I fire up a transaction.  It is assigned the DataSourceKey from my InvoicesCurrentBO (which happens to be "Aspire").  The CreateCommissions method simply loops through the invoices selected and adds an entry to the Commissions table, if applicable.  If all is well, the transaction should then commit.  It does not. 

I am thinking that a transaction cannot commit if there are no additions or changes to actually commit.  In the run I am testing there happens to be no commissions assigned, so there are no transactions to commit.  Should I be checking for that first, then committing?  Or, is there something else going on?

Please help,
Bill

By Bill Cunnien - 6/17/2008

I am removing transactions within the code.  This would be a perfect place for them, but I cannot get them to work.

Once I removed them, I ran into another problem which I will address in a separate thread.

Bill

By Trent L. Taylor - 6/17/2008

Transactions are a commonly used portion of the framework, but the one thing ab out using trans is environment....so this is one of those things that you may have to supply a sample so that we can see first hnd what is going on.  Sorry Ermm
By Bill Cunnien - 6/18/2008

Ok, now that Edhy helped me solve an underlying issue with the primary keys and identity columns, I am going to attempt the use of transactions again.  Since many others are using these successfully, and I am just an average joe BigGrin, then these should work for me, too.  Their use in the current procedure will go a long way to help me when things get a little messed up.

Ya, I know, it is rare for things to get messed up, but we should always prepare for the remote possibility, eh? Wink

Hangin' in there,
Bill

By Bill Cunnien - 6/18/2008

As I am re-working the transaction thingy, I was wondering, can one transaction cover data changes on two different databases?
By Bill Cunnien - 6/18/2008

The docs are a little ambiguous on this issue (see Using Transactional Processing); however, it appears that only one DB is involved per transaction.  I am formulating this conclusion from the fact that a datasourcekey is passed to the transaction methods.  So, I will need to be a bit creative in order to get two transactions going...if the second gets rolledback, then the first must be rolledback, too.

In my simplified/summarized/clipped code, I was thinking of doing something like this:

private void UpdateFirstDB()
{
   
try
   
{
   
BusinessLayer.TransactionBegin(MyPrimaryBO.DataSourceKey, IsolationLevel.ReadCommitted);
    . . . do some processing on a bunch of BOs in the primary data source . . .
    UpdateSecondDB();
   
BusinessLayer.TransactionCommit(MyPrimaryBO.DataSourceKey);
    }
   
catch
   
{
       
BusinessLayer.TransactionRollback(MyPrimaryBO.DataSourceKey);
       
throw;
    }
}

The secondary transaction looks quite similar:

private void UpdateSecondDB()
{
   
try
   
{
   
BusinessLayer.TransactionBegin(MySecondaryBO.DataSourceKey, IsolationLevel.ReadCommitted);
    . . . do some processing on a bunch of BOs in the secondary data source . . .
   
BusinessLayer.TransactionCommit(MySecondaryBO.DataSourceKey);
    }
   
catch
   
{
       
BusinessLayer.TransactionRollback(MySecondaryBO.DataSourceKey);
       
throw;
    }
}

Since the second transaction is wrapped inside the first, then any errors from the second should 1) Rollback the second transaction; 2) Bubble up the error which is caught inside the first transaction; 3) Rollback the first transaction; 4) Run my unhandled exception routine.

Am I thinking clearly enough on this?
Bill

By Bill Cunnien - 6/18/2008

So far, the testing has gone quite well with this new code.  If anyone can see where I need improvements, please let me know.  Otherwise, I am running with this.

Thanks!
Bill