Author Topic: Transaction within a loop.  (Read 9330 times)

0 Members and 1 Guest are viewing this topic.

BillZndl

  • Guest
Transaction within a loop.
« on: September 22, 2014, 07:29:12 AM »
Question:
I can't find a good example of how to do this.
Which would be better having the transaction inside the loop or outside? Or doesn't it matter?
First example inside, the second example is outside: I'm thinking the first example is the best because if you break out of the loop, it's outside the transaction
but then I'm creating a transaction each time through the loop, so I'm thinking this is not so good. TIA.
Code - C#: [Select]
  1.                using (document.LockDocument())
  2.                 {                  
  3.                
  4.                     for (int i = 0; i < listBox2.Items.Count; i++)
  5.                     {
  6.                         SetProgBarStatus(i);
  7.  
  8.                         string str = listBox2.Items[i].ToString();
  9.  
  10.                         using(Database database = new Database(false, false))
  11.                         {
  12.  
  13.                         database.ReadDwgFile(str, FileOpenMode.OpenForReadAndAllShare, false, "");                        
  14.  
  15.                         using (Transaction transaction = document.TransactionManager.StartTransaction())
  16.                         {
  17.  
  18.                             BlockTable table = (BlockTable)transaction.GetObject(database.BlockTableId, OpenMode.ForRead);
  19.                             BlockTableRecord btr = (BlockTableRecord)transaction.GetObject(table[BlockTableRecord.ModelSpace], OpenMode.ForRead);
  20.  
  21.                             foreach (ObjectId oid in btr)
  22.                             {
  23.                                 if (oid.ObjectClass.DxfName == "TEXT")
  24.                                 {
  25.                                     DBText dbt = (DBText)transaction.GetObject(oid, OpenMode.ForRead);
  26.  
  27.                                     if (dbt.TextString.Contains(pnum) || dbt.TextString.Contains(pnum.ToUpper()))
  28.                                     {
  29.                                         items.Add(str);
  30.                                         break;
  31.                                     }
  32.                                 }
  33.                             }
  34.  
  35.                             transaction.Commit();
  36.                         }
  37.                             database.CloseInput(true);                    
  38.                         }
  39.  
  40.                         if (cts.IsCancellationRequested)
  41.                           {
  42.                                 break;
  43.                           }
  44.                     }                    
  45.                 }
Code - C#: [Select]
  1.                using (document.LockDocument())
  2.                 {
  3.                using (Transaction transaction = document.TransactionManager.StartTransaction())
  4.                  {                  
  5.                
  6.                     for (int i = 0; i < listBox2.Items.Count; i++)
  7.                     {
  8.                             if (cts.IsCancellationRequested)
  9.                             {
  10.                                 break;
  11.                             }
  12.                         SetProgBarStatus(i);
  13.  
  14.                         string str = listBox2.Items[i].ToString();
  15.  
  16.                         using(Database database = new Database(false, false))
  17.                         {
  18.  
  19.                         database.ReadDwgFile(str, FileOpenMode.OpenForReadAndAllShare, false, "");                
  20.  
  21.                             BlockTable table = (BlockTable)transaction.GetObject(database.BlockTableId, OpenMode.ForRead);
  22.                             BlockTableRecord btr = (BlockTableRecord)transaction.GetObject(table[BlockTableRecord.ModelSpace], OpenMode.ForRead);
  23.  
  24.                             foreach (ObjectId oid in btr)
  25.                             {
  26.                                 if (oid.ObjectClass.DxfName == "TEXT")
  27.                                 {
  28.                                     DBText dbt = (DBText)transaction.GetObject(oid, OpenMode.ForRead);
  29.  
  30.                                     if (dbt.TextString.Contains(pnum) || dbt.TextString.Contains(pnum.ToUpper()))
  31.                                     {
  32.                                         items.Add(str);
  33.                                         break;
  34.                                     }
  35.                                 }
  36.                             }
  37.                                                    
  38.                             database.CloseInput(true);                          
  39.                         }                  
  40.                          
  41.                     }
  42.                         transaction.Commit();
  43.                   }                    
  44.                 }

MexicanCustard

  • Swamp Rat
  • Posts: 705
Re: Transaction within a loop.
« Reply #1 on: September 22, 2014, 07:49:22 AM »
Outside the loop.  Create as few transaction as you have to.  There is a lot of overhead in a transaction (opened objects, undo file, etc.) and even more for nested transaction since AutoCad keeps track of transaction on an internal stack.
Revit 2019, AMEP 2019 64bit Win 10

BillZndl

  • Guest
Re: Transaction within a loop.
« Reply #2 on: September 22, 2014, 09:19:58 AM »
ok, Thanks!
All the examples showed opening one drawing for read then starting the transaction.
It made me wonder when I enclosed the code in a loop.

Just checking with the wrist watch:
Using one transaction (outside the loop) and 1871 drawings, it made a difference of 30 seconds (from 2 minutes to 1-1/2 minutes) to do the search compared to inside the loop. :)

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: Transaction within a loop.
« Reply #3 on: September 23, 2014, 09:31:00 AM »
Just noticed you're using the document transaction to work inside a side database. Use the side database's transaction

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: Transaction within a loop.
« Reply #4 on: September 23, 2014, 09:33:32 AM »
Also since in this case you aren't modifying the side database the call to close input is adding extra time.

BillZndl

  • Guest
Re: Transaction within a loop.
« Reply #5 on: September 23, 2014, 11:12:13 AM »
Just noticed you're using the document transaction to work inside a side database. Use the side database's transaction

I don't know how to do that.
I tried creating the new side database before the transaction and use it for the transaction,
but when I try to use that side database in the loop, it throws an "eRepeatedDwgRead" exception and subsequent fatal error.

Is there a way to reset the new side database so I could use it more than once inside the loop?

The only other way I can think of, is to create the transaction within the loop after creating a new side database each time through the loop,
but then I'm back to creating a new transaction each time through the loop.

Or, could I use : Database db = HostApplicationServices.WorkingDatabase; for the transaction and make a new side database for the ReadDwgFile?


TIA


BillZndl

  • Guest
Re: Transaction within a loop.
« Reply #6 on: September 23, 2014, 11:12:44 AM »
Also since in this case you aren't modifying the side database the call to close input is adding extra time.

Ah, thanks! I will eliminate that.

Jeff H

  • Needs a day job
  • Posts: 6144
Re: Transaction within a loop.
« Reply #7 on: September 23, 2014, 11:31:41 AM »
Just noticed you're using the document transaction to work inside a side database. Use the side database's transaction

I don't know how to do that.
I tried creating the new side database before the transaction and use it for the transaction,
but when I try to use that side database in the loop, it throws an "eRepeatedDwgRead" exception and subsequent fatal error.

Is there a way to reset the new side database so I could use it more than once inside the loop?

The only other way I can think of, is to create the transaction within the loop after creating a new side database each time through the loop,
but then I'm back to creating a new transaction each time through the loop.

Or, could I use : Database db = HostApplicationServices.WorkingDatabase; for the transaction and make a new side database for the ReadDwgFile?


TIA

A document can have a database a number of side databases associated with it.

If your just reading information from a file; then in the Database constructor the second parameter is if you want to associate a document with it, and I guess only reason is for undo mechanism. So passing true as second argument in Constructor new Database(false, true). You do not need to lock the document and might solve error.
Then use the database you ReadDwgFile into to start a transaction then do whatever looping required.

BillZndl

  • Guest
Re: Transaction within a loop.
« Reply #8 on: September 23, 2014, 11:49:55 AM »

ok, I can try that, Jeff.
The only problem I can see is that eventually, I will want to not only read (find the part number) but then replace what's found with a  new part number.
So it would be easier if I could use the same code to do one or the other... but not imperative..

I won't need an undo because the changes will be made on a list of drawings, then changes saved so the only way to "undo" would be to run the list again.


Just noticed you're using the document transaction to work inside a side database. Use the side database's transaction

I don't know how to do that.
I tried creating the new side database before the transaction and use it for the transaction,
but when I try to use that side database in the loop, it throws an "eRepeatedDwgRead" exception and subsequent fatal error.

Is there a way to reset the new side database so I could use it more than once inside the loop?

The only other way I can think of, is to create the transaction within the loop after creating a new side database each time through the loop,
but then I'm back to creating a new transaction each time through the loop.

Or, could I use : Database db = HostApplicationServices.WorkingDatabase; for the transaction and make a new side database for the ReadDwgFile?


TIA

A document can have a database a number of side databases associated with it.

If your just reading information from a file; then in the Database constructor the second parameter is if you want to associate a document with it, and I guess only reason is for undo mechanism. So passing true as second argument in Constructor new Database(false, true). You do not need to lock the document and might solve error.
Then use the database you ReadDwgFile into to start a transaction then do whatever looping required.
« Last Edit: September 23, 2014, 11:52:57 AM by BillZndl »

BillZndl

  • Guest
Re: Transaction within a loop.
« Reply #9 on: September 23, 2014, 03:08:26 PM »
If your just reading information from a file; then in the Database constructor the second parameter is if you want to associate a document with it, and I guess only reason is for undo mechanism. So passing true as second argument in Constructor new Database(false, true). You do not need to lock the document and might solve error.

nope, got the same error. just an FYI.

Anyways, here's what I did for the search & replace.
I'll just run a separate block of code, no frills.
Comes in handy when they come up with a new part to replace an existing one that's the same in all respects except for the part number.  :?

Code - C#: [Select]
  1. using Autodesk.AutoCAD.ApplicationServices;
  2. using Autodesk.AutoCAD.DatabaseServices;
  3. using AcadApp = Autodesk.AutoCAD.ApplicationServices.Application;
  4.  
  5. namespace FileManagerDialog
  6. {
  7.  
  8.     class FixPartNumbers
  9.     {
  10.         /// <summary>
  11.         /// MassReplace, searches the database of drawings listed to find if string exists.
  12.         /// Replaces text with new text.
  13.         /// </summary>  
  14.  
  15.         public void MassReplace(string[] filelist, string strToFind, string strToUse)
  16.         {
  17.             Document document = AcadApp.DocumentManager.MdiActiveDocument;
  18.  
  19.             strToFind = strToFind.ToUpper();
  20.  
  21.             using (document.LockDocument())
  22.             {
  23.                 for (int i = 0; i < filelist.Length; i++)
  24.                 {
  25.                     string str = filelist[i].ToString();
  26.  
  27.                     using (Database database = new Database(false, false))
  28.                     {
  29.                         database.ReadDwgFile(str, FileOpenMode.OpenForReadAndWriteNoShare, false, "");
  30.                         database.CloseInput(true);
  31.  
  32.                         using (Transaction transaction = database.TransactionManager.StartTransaction())
  33.                         {
  34.                             BlockTable table = (BlockTable)transaction.GetObject(database.BlockTableId, OpenMode.ForWrite);
  35.                             BlockTableRecord btr = (BlockTableRecord)transaction.GetObject(table[BlockTableRecord.ModelSpace], OpenMode.ForWrite);
  36.  
  37.                             foreach (ObjectId oid in btr)
  38.                             {
  39.                                 if (oid.ObjectClass.DxfName == "TEXT")
  40.                                 {
  41.                                     DBText dbt = (DBText)transaction.GetObject(oid, OpenMode.ForRead);
  42.  
  43.                                     if (dbt.TextString.Contains(strToFind))
  44.                                     {
  45.                                         dbt.UpgradeOpen();
  46.                                         string oldStr = dbt.TextString;
  47.                                         string newStr = oldStr.Replace(strToFind, strToUse);
  48.                                         dbt.TextString = newStr;
  49.                                     }
  50.                                 }
  51.                             }
  52.  
  53.                             transaction.Commit();
  54.                         }
  55.  
  56.                         database.SaveAs(database.Filename, DwgVersion.Current);
  57.                     }                    
  58.                 }
  59.             }
  60.         }
  61.     }      
  62. }
  63.  

MexicanCustard

  • Swamp Rat
  • Posts: 705
Re: Transaction within a loop.
« Reply #10 on: September 24, 2014, 08:14:41 AM »
My two cents.

Why Lock the Document? You aren't doing anything with it.
Code - C#: [Select]
  1.             using (document.LockDocument())

Why ToString()? Elements of array are already strings?
Code - C#: [Select]
  1. string str = filelist[i].ToString();
Revit 2019, AMEP 2019 64bit Win 10

BillZndl

  • Guest
Re: Transaction within a loop.
« Reply #11 on: September 24, 2014, 09:19:14 AM »
My two cents.

Why Lock the Document? You aren't doing anything with it.
Code - C#: [Select]
  1.             using (document.LockDocument())

Good question. (This is for the code used for the "search only" code that doesn't change the databases.)
Jeff and all examples I could find said that if I used ReadDwgFile(false, true) I wouldn't need to lock the document.
But when I ran the code, i would get an "eNotFromThisDocument" error and subsequent fatal error with associated crash of Acad.
So I added the document lock back in.
After I read your post, I tried using ReadDwgFile(false, false) with no document lock and it ran fine. Why? dunno.
So I'm taking the lock out of the code.

Edit: I think I'll have to leave the doc lock in the code that changes the text strings , wouldn't I?
Edit#2: nope, not needed. Thanks!


Why ToString()? Elements of array are already strings?
Code - C#: [Select]
  1. string str = filelist[i].ToString();

Ah, good catch. This was copied from the other block of search code, where I was reading the listBox.Items, which needed that cast.
I get pulled in a lot of different directions around here so it can take me a while to go over the code and catch all that stuff.

Thanks!
« Last Edit: September 24, 2014, 09:27:37 AM by BillZndl »

Keith Brown

  • Swamp Rat
  • Posts: 601
Re: Transaction within a loop.
« Reply #12 on: September 24, 2014, 09:47:06 AM »
Why ToString()? Elements of array are already strings?
Code - C#: [Select]
  1. string str = filelist[i].ToString();

Ah, good catch. This was copied from the other block of search code, where I was reading the listBox.Items, which needed that cast.
I get pulled in a lot of different directions around here so it can take me a while to go over the code and catch all that stuff.

Thanks!


I know it costs alot of money ($250) but this is the kind of thing that ReSharper is really good at informing you of.   Some of the best money i ever spent.
Keith Brown | AutoCAD MEP Blog | RSS Feed
AutoCAD MEP 2014 / Revit MEP 2014 / EastCoast CAD/CAM addon / Visual Studio 2013

BillZndl

  • Guest
Re: Transaction within a loop.
« Reply #13 on: September 24, 2014, 10:47:41 AM »
I know it costs alot of money ($250) but this is the kind of thing that ReSharper is really good at informing you of.   Some of the best money i ever spent.

Thanks!
I would love to have tools like that!
My unofficial role as "CAD  manager" leaves me slam dunking things most of the time.
I never know when I'll be directed to go do something else, or for how long + alot of distractions.

My main job is mass producing AutoCAD drawings for assembly line models in the marine industry (25 yrs now since DOS version '89?).
I love codeing and drawing so finding ways to automate things has always intrigued me.

So until something else comes along, I'll be doing what I can.

Thanks, for all the help!


Jeff H

  • Needs a day job
  • Posts: 6144
Re: Transaction within a loop.
« Reply #14 on: September 24, 2014, 04:03:11 PM »
I have no problems using Database(false, true) constructor and the only time I have to lock the document when updating a side database is using 
Database(false, false) from Application context.
 
So if  Database(false, false)  used from a modeless dialog or a command with CommandFlags.Session then I have to lock document.