Why commiting transaction is a must even for read only operation ( in nunit test, that is)?

Why commiting transaction is a must even for read only operation ( in nunit test, that is)?

soonhui
Advisor Advisor
1,137 Views
12 Replies
Message 1 of 13

Why commiting transaction is a must even for read only operation ( in nunit test, that is)?

soonhui
Advisor
Advisor

By using @CADbloke 's excellent CADtest, I've finally managed to get my automated testing going. But I do have a problem with using Transaction in the context of this unit test framework.

 

The issue here lies with the line tr.Commit():

 

 

 

 

 

 

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading.Tasks;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
#if (ACAD2006 || ACAD2007 || ACAD2008 || ACAD2009|| ACAD2010|| ACAD2011 || ACAD2012)
    using Autodesk.AutoCAD.ApplicationServices;
    using Application = Autodesk.AutoCAD.ApplicationServices.Application;
#else
using Autodesk.AutoCAD.ApplicationServices.Core;
using Application = Autodesk.AutoCAD.ApplicationServices.Core.Application;
#endif
using CADtest.Helpers;
using NUnit.Framework;
using System.IO;

namespace CADTestRunner
{
    public abstract class BaseTests
    {
        protected void ExecuteActionDWG(String pDrawingPath, params Action<Database, Transaction>[] pActionList)
        {
            bool buildDefaultDrawing;

            if (String.IsNullOrEmpty(pDrawingPath))
            {
                buildDefaultDrawing = true;
            }
            else
            {
                buildDefaultDrawing = false;

                if (!File.Exists(pDrawingPath))
                {
                    Assert.Fail("The file '{0}' doesn't exist", pDrawingPath);
                    return;
                }
            }

            Exception exceptionToThrown = null;

            Document doc = Application.DocumentManager.MdiActiveDocument;
            using (doc.LockDocument())
            {
                using (Database db = new Database(buildDefaultDrawing, false))
                {
                    if (!String.IsNullOrEmpty(pDrawingPath))
                        db.ReadDwgFile(pDrawingPath, FileOpenMode.OpenForReadAndWriteNoShare, true, null);

                    using (new WorkingDatabaseSwitcher(db))
                    {
                        foreach (var item in pActionList)
                        {
                            using (Transaction tr = db.TransactionManager.StartTransaction())
                            {
                                try
                                {
                                    item(db, tr);
                                }
                                catch (Exception ex)
                                {
                                    exceptionToThrown = ex;
                                    tr.Commit();//comment it will cause the test runner to hang, if an exception is thrown

                                    //stop execution of actions
                                    break;
                                }

                                tr.Commit();  //comment it will cause the test runner to hang

                            }

                        }

                    }

                }

            }

            //throw exception outside of transaction
            //Sometimes Autocad crashes when exception throws
            if (exceptionToThrown != null)
            { 
                throw exceptionToThrown;
            }
        }

    }
}

 

 

 

 

 

 

And here's the parallel stack trace:

Hang thread.png

 

I notice that if you disable all of the test case except the readonly one ( ie: just Ignore all test cases except Test_method_name), and you comment the above tr.Commit(), it should still work because Commit only commits your changes. If you have no changes ( this is what readonly means, right?), then it shouldn't matter whether you commit or not.

 

But under the test environment, it just hangs at disposing the transaction. Under normal environment where I launch AutoCAD/Civil 3D properly, then commenting the tr.Commit() works just fine.

 

I have another test case ( which is way more complicated than this one), it also involves only readonly operation, but when disposing the transaction, the code crashes with AccessViolationException instead of hang.

 

I'm using NUnit 3.13.3/NUnitLite 3.13.3/Civil 3D 2023.1 version. I just don't know whether the problem occurs at AutoCAD/Civil 3D end or NUnit end, someone perhaps can shed some lights on this?

 

 

 

 

##########

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)
1,138 Views
12 Replies
Replies (12)
Message 2 of 13

_gile
Consultant
Consultant

Hi,

If you do not commit a transaction, it is automatically aborted (i.e. everything within the transaction is rolled back), so, committing is always cheaper even when there's no change.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 3 of 13

soonhui
Advisor
Advisor

@_gile , thanks for the tips.

 

But... the program shouldn't hang ( or crash) if I don't commit, as long as my operations are strictly read only. Right?

 

That's why if it hangs, I would be curious to know how to fix it, if it's fixable.

##########

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 13

_gile
Consultant
Consultant

Excuse me if I wasn't clear.

If you do not explicitly commit the transaction, the transaction automatically aborts.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 5 of 13

soonhui
Advisor
Advisor

@_gile , the issue has nothing to do with whether the transaction should be aborted or not if it's not committed.

 

The problem, as I mentioned in the beginning, is that if I don't call Transaction.Commit(), then in this particular context, it will hang the whole test program ( you can reproduce the problem following my steps above). But it really shouldn't, and it wouldn't if I run the AutoCAD program normally with a C# program ( instead of running via NUnit context like this one). 

##########

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 6 of 13

Alfred.NESWADBA
Consultant
Consultant

Hi,

 

>> via NUnit context 

I don't have experience with this, however from your code above:

If you code runs into an exception then tr.Commit is called twice, which does not work.

 

- alfred -

------------------------------------------------------------------------------------
Alfred NESWADBA
ISH-Solutions GmbH / Ingenieur Studio HOLLAUS
www.ish-solutions.at ... blog.ish-solutions.at ... LinkedIn ... CDay 2026
------------------------------------------------------------------------------------

(not an Autodesk consultant)
0 Likes
Message 7 of 13

soonhui
Advisor
Advisor

@Alfred.NESWADBA , you misread the code. The break statement will only skip the second tr.Commit() statement because it will exit from the for statement immediately after it is being reached.  So tr.Commit() won't be called twice. 

 

Anyway my question has nothing to do with tr.Commit() but rather the absence of it will cause the test program to hang. 

##########

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 8 of 13

Alfred.NESWADBA
Consultant
Consultant

Hi,

 

>> you misread the code. The break statement will only skip the

>> second tr.Commit() statement because it will exit

Yep, you are correct with this, my fault, sorry!

However (not sure about this) ... if the line with "break" exits the for-each loop ... will the transaction then be disposed (as you are also exiting the "using ..." statement) ... one of the reasons why I never exit a loop with "break"

 

- alfred -

 

------------------------------------------------------------------------------------
Alfred NESWADBA
ISH-Solutions GmbH / Ingenieur Studio HOLLAUS
www.ish-solutions.at ... blog.ish-solutions.at ... LinkedIn ... CDay 2026
------------------------------------------------------------------------------------

(not an Autodesk consultant)
0 Likes
Message 9 of 13

soonhui
Advisor
Advisor

Let's get back to the main issue, I've updated the parallel stack trace, hopefully it's helpful.

 

Hang thread.png

##########

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 10 of 13

nshupeFMPE3
Advocate
Advocate

I'm very interested if you can get this to work. I tried many times with no luck. 
Sorry if it is not of much help, as I dont remember all the details, its been a while. 
But I think I finally came to the conclusion that AutoCAD didn't like that an outside program (NUnit) modifying things at the same time as AutoCAD. 

0 Likes
Message 11 of 13

soonhui
Advisor
Advisor

@nshupeFMPE3 , I can get it to work if I run the tests in acad.exe, and it also sort of works in accoreconsole.exe. 

 

But I do encounter a bug when I'm running it in accoreconsole.exe, though the tests should still run just fine.

 

It should be noted that the previous versions of AcCoreConsole doesn't have this problem as far back in 2015( though it does have other problem. In this sense, 2015 version is worse than 2013 version). The quality of Autodesk products seem to be declining, and the regression bugs in AcCoreConsole is an indicative of that. 

##########

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 12 of 13

daniel_cadext
Advisor
Advisor
You don't need to use a transaction at all if you don't want to, you can use open and using statements, or OpenCloseTransaction just closes with no abort
Python for AutoCAD, Python wrappers for ARX https://github.com/CEXT-Dan/PyRx
0 Likes
Message 13 of 13

soonhui
Advisor
Advisor
Accepted solution

To answer my own question, the Commit is a MUST even for Read operation, only if I use NoDocument=false in the below code:

 

 using (Database db = new Database(buildDefaultDrawing, false)) //change the second parameter to true means no need for Transaction.Commit()

 

If I change the second parameter to true, then I don't need the Transaction.Commit() for the READ operation. 

##########

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