Is the same Entity reference valid across nested transaction?

Is the same Entity reference valid across nested transaction?

soonhui
Advisor Advisor
575 Views
10 Replies
Message 1 of 11

Is the same Entity reference valid across nested transaction?

soonhui
Advisor
Advisor

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)?

##########

Ngu Soon Hui

##########

I'm the Benevolent Dictator for Life for MiTS Software. Read more here


I also setup Civil WHIZ in order to share what I learnt about Civil 3D
0 Likes
Accepted solutions (1)
576 Views
10 Replies
Replies (10)
Message 2 of 11

_gile
Consultant
Consultant

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).

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 3 of 11

soonhui
Advisor
Advisor

@_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?

##########

Ngu Soon Hui

##########

I'm the Benevolent Dictator for Life for MiTS Software. Read more here


I also setup Civil WHIZ in order to share what I learnt about Civil 3D
0 Likes
Message 4 of 11

cuongtk2
Advocate
Advocate

Refer to local scope.

0 Likes
Message 5 of 11

_gile
Consultant
Consultant

@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.

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 6 of 11

soonhui
Advisor
Advisor

@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?

##########

Ngu Soon Hui

##########

I'm the Benevolent Dictator for Life for MiTS Software. Read more here


I also setup Civil WHIZ in order to share what I learnt about Civil 3D
0 Likes
Message 7 of 11

_gile
Consultant
Consultant

@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.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 8 of 11

cuongtk2
Advocate
Advocate
Accepted solution

 

 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
 }

 

Message 9 of 11

_gile
Consultant
Consultant

@cuongtk2 

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.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 10 of 11

ActivistInvestor
Mentor
Mentor

@_gile wrote:

@cuongtk2 

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. 

Message 11 of 11

_gile
Consultant
Consultant

@ActivistInvestor Thanks. I'm always glad to learn a new thing.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes