TheSwamp
Code Red => .NET => Topic started by: Chuck Gabriel on July 15, 2008, 08:43:06 AM
-
Is there any problem with returning from within a transaction like below?
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.
-
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
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
-
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.
-
Is there any problem with returning from within a transaction like below?
No.
-
Is there any problem with returning from within a transaction like below?
No.
Thanks Spike. :-)
Good to see you back, by the way.
-
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.
-
Is there any problem with returning from within a transaction like below?
No.
Welcome back Spike LE :-D
-
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.
-
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
-
Heh...agreed, or even C++ like scoping...ah well.
I would pen your function like so:
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();
}
}
}
}
-
Nice Example Glenn, that looks like it would take care of any issues that might arise.
Thank you