.NET
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Transaction

13 REPLIES 13
Reply
Message 1 of 14
wen30
1221 Views, 13 Replies

Transaction

Hi , is there any property or anything to check if any transaction is active so i can use back the same transaction? another transaction triggered by another function will cause AutoCAD to crash. Thanks a lot.

 

for example,

both test1 and test2 require a transaction test1 is used by other function as well but when test1 is triggered by test2, autoCAD crash. 

 

public void test1 ()
{
using (Transaction aTrans = aDocument.TransactionManager.StartTransaction())
{
}
}

public void test2()
{
using (Transaction aTrans = aDocument.TransactionManager.StartTransaction())
{
test1();
}
}

 

 

13 REPLIES 13
Message 2 of 14
DECH0002
in reply to: wen30

I don't think that creating another transaction is the problem. Can you really see that in the debuger, that it is the StartTransaction call in test1, when called from test2?

 

Howerver, as a workaround, you may want to pass aTrans as a parameter to test1. If you pass null (from any call outside test2), create a new one. If it is not null, use it.

 

Best,

Martin

Message 3 of 14
norman.yuan
in reply to: wen30

Nested transactions themself do not crash AutoCAD and can be used if necessary. It is other factor/factors in your code that crash AutoCAD. For example, this code worked OK:

 

        [CommandMethod("TransTest",CommandFlags.UsePickSet)]
        public static void TestRun()
        {
            Document dwg = Autodesk.AutoCAD.ApplicationServices.
                Application.DocumentManager.MdiActiveDocument;

            //Make sure there are at least 2 entities selected
            PromptSelectionResult res=dwg.Editor.SelectImplied();
            if (res.Status != PromptStatus.OK) return;

            DoTransaction(dwg,res.Value.GetObjectIds());
        }

        private static void DoTransaction(Document dwg, ObjectId[] ids)
        {
            using (Transaction tran = dwg.Database.TransactionManager.StartTransaction())
            {
                Entity ent = (Entity)tran.GetObject(ids[0], OpenMode.ForRead);
                dwg.Editor.WriteMessage("\nFirst entity opened in TRANS1 is {0}", ent.GetType().Name);

                DoNestedTransaction(dwg, ids);

                tran.Commit();
            }
        }

        private static void DoNestedTransaction(Document dwg, ObjectId[] ids)
        {
            using (Transaction tran = dwg.Database.TransactionManager.StartTransaction())
            {
                Entity ent = (Entity)tran.GetObject(ids[ids.Length-1], OpenMode.ForRead);
                dwg.Editor.WriteMessage("\nLast entity opened in TRANS2 is {0}", ent.GetType().Name);

                tran.Commit();
            }
        }

 

So, it depends what you do in the function that wrapped with transaction. For example, were you trying to open the same DBObject in more than 1 transaction?

Norman Yuan

Drive CAD With Code

EESignature

Message 4 of 14
wen30
in reply to: wen30

The transaction failed in the event function. OnObjectModified, how am i going to Commit() if i change and use the ObjectId.Open?  

Message 5 of 14
norman.yuan
in reply to: wen30

I assume you are taking about Database.ObjectModified event?

 

Were you trying to modify DBObject (either passed in by e.DbObject, or other DBObject in the database) in the event handler? If so, it would cause Database.ObjectModified event fires again, then your code in the event handler runs again, then ObjectModified event fires...then..again and again, endless.

 

Well, you may have some sort of flag set, so that your code in the ObjectModified event handler would only run once in certain condition, so to avoid the endless dead loop. Nonetheless, in many cases, change/modify DBObject in Database.ObjectModified event handler is dangerous.

 

By the way, If you do something like:

 

//Somewhere, you registered the event handler

theDb.ObjectModified += new ObjectEventHandler(DB_ObjectModified);

 

void DoTransaction()

{

    using (Transaction tran=....StartTransaction())

    {

        //Open DBObject and change it

        tran.Commit();

    }

}

 

void DB_ObjectModified(object sender, ObjectEventArgs e)

{

    ....

    //Start another transaction, only when a flag is not set

    using (Transaction tran=....StartTransaction())

    {

 

    }

}

 

The transaction in the ObjectModified event handler IS NOT a nested Transaction underneath the other transaction, because the ObjectModified event ONLY fires when the first transaction is commited (so the DBObject has been modified).

 

So, if your code crash AutoCAD, is something you do logically wrong, not because of transaction. Without knowing what exactly you want to achieve and what the code is, it is hard to comment more.

Norman Yuan

Drive CAD With Code

EESignature

Message 6 of 14
norman.yuan
in reply to: norman.yuan

The other often being overlooked issue is that the event handler registered to the database (e.g. theDB.ObjectModified +=....) is not removed correctly at some point of the code's process, and it could be registered many times by repeatedly run a piece of improperly written code, thus even one DBObject is modified, the event could fire many times and handled many times. This may cause some issues, depending on the code in the event handler.

Norman Yuan

Drive CAD With Code

EESignature

Message 7 of 14
wen30
in reply to: wen30

The code is really simple, i want to move all the linked object together if one of it is moved. The first linked object is moved using a transaction so the transaction in the event cannot be started. 

 

 

        void OnObjectModified(object sender, ObjectEventArgs e)
        {
            try
            {
                Database aDatabase = (Database)sender;
                if (e.DBObject.GetType() == typeof(BlockReference))
                {
                BlockReference aBlockReference = (BlockReference)e.DBObject;
                Point3d aPoint3d_Old = alinkobject.Old_Position;
                Point3d aPoint3d_Current = aBlockReference.Position;
                Vector3d aVector3d = aPoint3d_Current - aPoint3d_Old;
                if ((aVector3d.Length < 0.00001 || aVector3d.Length > 0.00001) && aVector3d.Length != 0.0)
                {
                    using (DocumentLock aDocumentLock = aProjectDocument.LockDocument())
                    {
                        using (Transaction aTransaction = aProjectDocument.TransactionManager.StartTransaction())
                        {
                            foreach (linkobject alinkobject in clinkobject)
                            {
                                BlockReference alinkobject = (BlockReference)alinkobject.ObjectID.Open(OpenMode.ForRead);
                                alinkobject.UpgradeOpen();
                                alinkobject.TransformBy(Matrix3d.Displacement(aVector3d));
                            }
                        }
                        aTransaction.Commit();
                    }
                }
                }
            }
            catch
            {
            }
        }

 

 

Message 8 of 14
DECH0002
in reply to: wen30

First of all, you should commit the transaction inside the using directive.

 

                        using (Transaction aTransaction = aProjectDocument.TransactionManager.StartTransacti​on())
                        {
                            foreach (linkobject alinkobject in clinkobject)
                            {
                                BlockReference alinkobject = (BlockReference)alinkobject.ObjectID.Open(OpenMode​.ForRead);
                                alinkobject.UpgradeOpen();
                                alinkobject.TransformBy(Matrix3d.Displacement(aVec​tor3d));
                            }
                            aTransaction.Commit();
} aTransaction.Commit();
 

 

Your using directive compiles into the following code:

 

 

Transaction aTransaction = aProjectDocument.TransactionManager.StartTransacti​on()
try
{
     foreach (linkobject alinkobject in clinkobject)
     {
           BlockReference alinkobject = (BlockReference)alinkobject.ObjectID.Open(OpenMode​.ForRead);
           alinkobject.UpgradeOpen();
           alinkobject.TransformBy(Matrix3d.Displacement(aVec​tor3d));
     }
}
finally
{
    aTransaction.Dispose();
}
aTransaction.Commit(); 

 You are committing a disposed object, thought.

 

Tags (1)
Message 9 of 14
DECH0002
in reply to: DECH0002

Another five cents from me, not attached to your current problem but with view of readability:

 

                    using (DocumentLock aDocumentLock = aProjectDocument.LockDocument())
                    using (Transaction aTransaction = aProjectDocument.TransactionManager.StartTransacti​on())
                    {
                        foreach (linkobject alinkobject in clinkobject)
                        {
                            BlockReference alinkobject = (BlockReference)alinkobject.ObjectID.Open(OpenMode​.ForRead);
                            alinkobject.UpgradeOpen();
                            alinkobject.TransformBy(Matrix3d.Displacement(aVec​tor3d));
                        }
                        aTransaction.Commit();
                    }

 

It's the same.

Tags (1)
Message 10 of 14
norman.yuan
in reply to: wen30

Have you debugged the code, stepping through the code in the event handler? Which line of code causes the crash? Or is that TransactionManager.StartTransaction() causes the crash (since you said: "the transaction in the event cannot be started.")?

 

As the code in my previous reply showed, a transaction can be started in the ObjectModified event handler, at least my simple code example works as expected. So, you need to clarify what does that mean "the transaction in the event cannot be started".

 

Is it possible that at least the first time the code in the event handler run past the StartTransaction()? The reason I ask this is because I do see possible logic issues with your cdoe:

 

1. Is this your intention that ANY blockreference in the database would result in all entities in "clinkobject" colletion to be moved? Well it could be so if all BlockReferences in the drawing is your linked objects. If there is BlockReference not belonging to the linked entities, your code should identify it before going on to change other linked entities.

 

2. the "foreach...in clinkobject" in wrapped in the transaction is likely wrong: I'd imagine the first linked object that triggers the transaction in ObjectModified event handler should also be in the collection "clinkobject", should it not? If so, then that very object is then moved again, that in tern causes the event fires and code in the event handler to run again. Actually, even the first object is not in the collection, the chain action would still occurs endlessly, as long as one of the linked object is BlockReference.

 

Your code would only work when the first linked object, which triggers the ObjectModified event, is the ONLY BlockReference in the drawing AND IS NOT IN the "clinkobject" collection. Otherwise the code would run endlessly to crash AutoCAD.

 

 

Norman Yuan

Drive CAD With Code

EESignature

Message 11 of 14
wen30
in reply to: wen30

sorry, i missplace the Commit() when i pasted the code here.. the block reference actually got name if (aBlockreference.Name == "first"). i keep on changing the code.. that's why i miss out something here but basically this is how it works..  i debug and went through all the steps and the code goes to catch when using (Transaction aTransaction = aProjectDocument.TransactionManager.StartTransaction())

Message 12 of 14
wen30
in reply to: wen30

this event actually works when i manually pull the first object using my mouse but failed when it is trigger by the another transaction which move the first object.

Message 13 of 14
wen30
in reply to: wen30

this is the error msg i got. i cannot start a transaction in the event when the object modified event is triggered by another transaction.

 

for example i moved the object using a transaction. 

 

 

using (Transaction aTransaction = aProjectDocument.TransactionManager.StartTransaction())
{
Entity aEntity = (Entity)aTransaction.GetObject(aObject.ObjectId, OpenMode.ForRead);
aEntity.UpgradeOpen();
aEntity.TransformBy(aMatrix3d);
aTransaction.Commit();
}

 

 

i used a simple code which doesn't include anything in it and insert a break point at there.

 

 

void OnObjectModified(object sender, ObjectEventArgs e)
{
try
{
Database aDatabase = (Database)sender;
using (Transaction aTrans = aDatabase.TransactionManager.StartTransaction())
{ }
}
catch
{
}

 

and the step goes to catch. and when i look at the output i get this

 

 

A first chance exception of type 'System.NullReferenceException' occurred in AcdbMgd.dll

 

anybody knows? 

Message 14 of 14
DECH0002
in reply to: wen30

I can confirm this behaviour. I tested it on a simple wizzard-generated sceleton without any overhead.

 

However, try to use the following event code:

 

void OnObjectModified(object sender, ObjectEventArgs e)
{
    try
    {
        Database oDatabase = sender as Database;
        if (oDatabase != null)
        {
             // Modify this code to make shure, TopTransaction is valid.
             Entity oEntity = oDatabase.TransactionManager.TopTransaction.GetObject(aObjectId, OpenMode.ForWrite) as Entity;
             if (oEntity != null)
             {
                 // Do it.
             }
        }
    }
    catch (System.Exception ex)
    {
    }
}

 And avoid, I mean really avoid it to modify the notifying entity here.

Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk DevCon in Munich May 28-29th


Autodesk Design & Make Report

”Boost