Author Topic: Transaction Do's and Dont's  (Read 3704 times)

0 Members and 1 Guest are viewing this topic.

Chuck Gabriel

  • Guest
Transaction Do's and Dont's
« on: July 15, 2008, 08:43:06 AM »
Is there any problem with returning from within a transaction like below?

Code: [Select]
void doSomeStuff()
{
    Database db = Application.DocumentManager.MdiActiveDocument.Database;
    using (Transaction tr = db.TransactionManager.StartTransaction())
    {
        // Do preliminary stuff
        if (!goingAccordingToPlan) return; // Is this a no no?

        // Make changes to the database
        tr.Commit();
    }
}

It seems to me that the transaction will abort and clean itself up when it goes out of scope, but I've been wrong frequently enough that I'd like to make sure.

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8722
  • AKA Daniel
Re: Transaction Do's and Dont's
« Reply #1 on: July 15, 2008, 09:48:31 AM »
Since you have the transaction wrapped in a ‘using ‘ statement, the transaction  will be disposed of once it goes out of scope, even if there is an exception.

One of the things I have been wondering about is, do I need to make sure that my newly created objects are disposed of in the case of an exception before the call to commit?  If so maybe it might be better to throw an exception rather than just using return, at least I would have a chance to dispose of the objects.  Consider this example

Code: [Select]
namespace ExecMethod
{
  public class Commands
  {

    [CommandMethod("doit")]
    static public void doit()
    {
      Editor ed = AcAp.Application.DocumentManager.MdiActiveDocument.Editor;
      Database db = HostApplicationServices.WorkingDatabase;

      bool errorFlag = false;

      Line ln = new Line(new Point3d(0, 0, 0), new Point3d(100, 0, 0));

      try
      {

        AcDb.TransactionManager manager = db.TransactionManager;
        using (Transaction transaction = manager.StartTransaction())
        {

          BlockTableRecord record = (BlockTableRecord)transaction.GetObject
                         (SymbolUtilityServices.GetBlockModelSpaceId(db), OpenMode.ForWrite, false);

       
          record.AppendEntity(ln);
          transaction.AddNewlyCreatedDBObject(ln, true);

          errorFlag = true; // Oh No

          if (errorFlag)
          {
            throw new System.InvalidOperationException();
          }

          transaction.Commit();
        }

      }
      catch (System.InvalidOperationException ex)
      {
        ed.WriteMessage("\nHere in this Exception we can clean up ln\n");
        ed.WriteMessage(ex.Message);
        ed.WriteMessage(ex.StackTrace);

        ln.Dispose(); //<---- Dispose
      }
      catch (System.Exception ex)
      {
        ed.WriteMessage(ex.Message);
        ed.WriteMessage(ex.StackTrace);
      }
    }
  }
}

Edit;  in your example, the transaction would be aborted, if something went wrong
« Last Edit: July 15, 2008, 10:06:34 AM by Daniel »

Chuck Gabriel

  • Guest
Re: Transaction Do's and Dont's
« Reply #2 on: July 15, 2008, 10:12:00 AM »
Going by Kean Walmsley's advice, it looks to me like it isn't necessary to manually dispose of ln, because tr takes over its management when you call AddNewlyCreatedDBObject.

BTW - I didn't know there was a managed wrapper for AcDbHostApplicationServices.  That will save me quite a bit of typing in the future.

Spike Wilbury

  • Guest
Re: Transaction Do's and Dont's
« Reply #3 on: July 15, 2008, 10:13:33 AM »
Is there any problem with returning from within a transaction like below?

No.

Chuck Gabriel

  • Guest
Re: Transaction Do's and Dont's
« Reply #4 on: July 15, 2008, 10:47:07 AM »
Is there any problem with returning from within a transaction like below?

No.

Thanks Spike.  :-)

Good to see you back, by the way.

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8722
  • AKA Daniel
Re: Transaction Do's and Dont's
« Reply #5 on: July 15, 2008, 12:13:39 PM »
Quote
Going by Kean Walmsley's advice, it looks to me like it isn't necessary to manually dispose of ln, because tr takes over its management when you call AddNewlyCreatedDBObject.

Ah ok, you’re right, after re-reading Kean’s article on disposing, I see that I am responsible for my new objects until they become managed by the transaction manager (via AddNewlyCreatedDBObject()). If an exception occurs before the transaction manager takes control, I should still manually dispose of the new objects.

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8722
  • AKA Daniel
Re: Transaction Do's and Dont's
« Reply #6 on: July 15, 2008, 12:15:12 PM »
Is there any problem with returning from within a transaction like below?

No.

Welcome back Spike LE  :-D

Glenn R

  • Guest
Re: Transaction Do's and Dont's
« Reply #7 on: July 15, 2008, 01:02:08 PM »
Chuck,

No. Returning from the 'using' scope of a transaction will call abort on the transaction it it hasn't already been committed, then the 'using' will call Dispose() as it should.
From memory, the streams in .NET automatically call Close() once they're disposed as well - Close() being synonymous with Dispose in this case.

Dan,

ANYTHING that you 'new' and the dbase doesn't take control of (by adding it in a transaction for instance) you must destroy, otherwise BAD things can and will happen.

Cheers,
Glenn.

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8722
  • AKA Daniel
Re: Transaction Do's and Dont's
« Reply #8 on: July 15, 2008, 01:06:45 PM »
ANYTHING that you 'new' and the dbase doesn't take control of (by adding it in a transaction for instance) you must destroy, otherwise BAD things can and will happen.

Sure would be nice if C# had smart pointers  :-D

Glenn R

  • Guest
Re: Transaction Do's and Dont's
« Reply #9 on: July 15, 2008, 01:14:57 PM »
Heh...agreed, or even C++ like scoping...ah well.

I would pen your function like so:

Code: [Select]
namespace ExecMethod
{
  public class Commands
  {

    [CommandMethod("doit")]
    static public void doit()
    {
      Editor ed = AcAp.Application.DocumentManager.MdiActiveDocument.Editor;
      Database db = HostApplicationServices.WorkingDatabase;

  Line ln = null;
  Transaction tr = null;

  try
  {

  AcDb.TransactionManager manager = db.TransactionManager;
  tr = manager.StartTransaction();

  BlockTableRecord record = (BlockTableRecord)transaction.GetObject
   (SymbolUtilityServices.GetBlockModelSpaceId(db), OpenMode.ForWrite, false);

  if (someCondition)
  ln = new Line(new Point3d(0, 0, 0), new Point3d(100, 0, 0));

  record.AppendEntity(ln);
  transaction.AddNewlyCreatedDBObject(ln, true);

  transaction.Commit();

  }
  catch (System.InvalidOperationException ex)
  {
  ed.WriteMessage("\nHere in this Exception we can clean up ln\n");
  ed.WriteMessage(ex.Message);
  ed.WriteMessage(ex.StackTrace);
  if (ln != null)
  ln.Dispose(); //<---- Dispose
  }
  catch (System.Exception ex)
  {
  ed.WriteMessage(ex.Message);
  ed.WriteMessage(ex.StackTrace);
  }
  finally
  {
  tr.Dispose();
  }
    }
  }
}

It's Alive!

  • Retired
  • Needs a day job
  • Posts: 8722
  • AKA Daniel
Re: Transaction Do's and Dont's
« Reply #10 on: July 15, 2008, 01:23:18 PM »
Nice Example Glenn, that looks like it would take care of any issues that might arise.
Thank you