Is the same Entity reference valid across nested transaction, all the time? I have code as such ( where I reuse not just the ObjectId of the line Entity, but also the entity itself).
[CommandMethod(nameof(NestedTransactions))]
public static void NestedTransactions()
{
Document doc = Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
using (Transaction trOuter = db.TransactionManager.StartTransaction())
{
// Create a line
Line line = new Line(new Point3d(0, 0, 0), new Point3d(10, 0, 0));
var btr = (BlockTableRecord)trOuter.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
btr.AppendEntity(line);
trOuter.AddNewlyCreatedDBObject(line, true);
// Inner transaction 1: Change color
using (Transaction trInner1 = db.TransactionManager.StartTransaction())
{
line.ColorIndex = 1; // Red
trInner1.Commit();
}
// Inner transaction 2: Change layer
using (Transaction trInner2 = db.TransactionManager.StartTransaction())
{
line.Layer = "A-BLDG";
trInner2.Commit();
}
trOuter.Commit();
}
ed.Regen();
}
It works, but I'm not sure whether it's always safe to do this? or I have to use ObjectId and get the actual entity from the reigning Transaction ( since ObjectId is the same within the lifetime of the drawing)?
Solved! Go to Solution.
Solved by cuongtk2. Go to Solution.
Hi,
The 'line' entity is available within the whole scope of the 'trOuter' transaction including nested transactions and methods called within this scope.
So, yes it is safe to do this (even nested transactions are absolutely useless in this example).
@_gile , I admit that I ask a useless question.
A more interesting question is, if I create a new Entity in the trInner1 ( let's call it l1), I can access l1 and use it normally in trInner2? Or I have to use l1.ObjectId to retriev back the same entity in trInner2?
@soonhui a écrit :
A more interesting question is, if I create a new Entity in the trInner1 ( let's call it l1), I can access l1 and use it normally in trInner2? Or I have to use l1.ObjectId to retriev back the same entity in trInner2?
Same reply as upper, 'l1' is only available in the scope of 'trInner1', in other words, between the openning curly bracket following the using statement where 'trinner1' is initialized and the corresponding closing curly bracket.
When we add a newly created onject to a transaction (using AddNewlyCreatedDBObject) this newly created object will be automatically disposed as soon as the transaction is disposed.
using (Transaction trInner1 = db.TransactionManager.StartTransaction())
{
// Create a line
Line l1 = new Line(new Point3d(0, 0, 0), new Point3d(10, 0, 0));
var btr = (BlockTableRecord)trInner1.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
btr.AppendEntity(l1);
// Add the newly created line to the transaction
trInner1.AddNewlyCreatedDBObject(l1, true);
trInner1.Commit();
} // <- Both 'trInner1' and 'l1' are disposed at the end of the using statement.
@cuongtk2 , @_gile , so if I move the declaration of l1 to outer scope ( or if I wrap the above code in a method and pass back l1 to the outer scope), then I can use l1 outer scope as usual, no problem?
@soonhui a écrit :
@cuongtk2 , @_gile , so if I move the declaration of l1 to outer scope ( or if I wrap the above code in a method and pass back l1 to the outer scope), then I can use l1 outer scope as usual, no problem?
No, despite the 'l1' variable is accessible, its value (the entity) has been disposed.
using (var trOuter = db.TransactionManager.StartTransaction())
{
Line l1 = null;
using (Transaction trInner1 = db.TransactionManager.StartTransaction())
{
// Create a line
l1 = new Line(new Point3d(0, 0, 0), new Point3d(10, 0, 0));
var btr = (BlockTableRecord)trInner1.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
btr.AppendEntity(l1);
// Add the newly created line to the transaction
trInner1.AddNewlyCreatedDBObject(l1, true);
trInner1.Commit();
}
// change l1 EndPoint
l1.EndPoint = new Point3d(5, 0, 0);
trOuter.Commit();
// l1.length = 5.0
}
The code you posted is not safe because 'l1' is disposed withe the transaction (line #13) and setting l1.EndPoint after that (line # 15) may crash AutoCAD (it depends on when the garbage collection occurs).
One more time, the rule is: never use a DBObject outside of the the scope of the transaction this object has been opened with or added to.
@_gile wrote:The code you posted is not safe because 'l1' is disposed withe the transaction (line #13) and setting l1.EndPoint after that (line # 15) may crash AutoCAD (it depends on when the garbage collection occurs).
One more time, the rule is: never use a DBObject outside of the the scope of the transaction this object has been opened with or added to.
Hi @_gile . As I'm sure you know, I have cited the same advise you offer above many times, however that is generally related to outermost transactions only.
Nested transactions are a different kind of animal. In fact, @cuongtk2 's code is perfectly safe, as explained by @artc2 in the thread I linked to above.
Can't find what you're looking for? Ask the community or share your knowledge.