Author Topic: Problem with active document and working database switching  (Read 6195 times)

0 Members and 1 Guest are viewing this topic.

Andrey Bushman

  • Swamp Rat
  • Posts: 864
I did the refactoring of my this code. I had  added the DatabaseSwitcher and DocumentSwitcher classes. The result of refactoring is:
Code - C#: [Select]
  1. // © Andrey Bushman, 2014
  2. // LayerTableRecordTesting.cs
  3. // Testing of case: http://www.theswamp.org/index.php?topic=46980.0
  4. using System;
  5. using System.IO;
  6. using System.Collections.Generic;
  7. using System.Linq;
  8. using Mb = MbUnit.Framework;
  9. using cad = Autodesk.AutoCAD.ApplicationServices.Application;
  10. using Ap = Autodesk.AutoCAD.ApplicationServices;
  11. using Db = Autodesk.AutoCAD.DatabaseServices;
  12. using Ed = Autodesk.AutoCAD.EditorInput;
  13. using Rt = Autodesk.AutoCAD.Runtime;
  14. using Hs = Autodesk.AutoCAD.DatabaseServices.HostApplicationServices;
  15. using Us = Autodesk.AutoCAD.DatabaseServices.SymbolUtilityServices;
  16.  
  17. namespace Bushman.CAD.Testing {
  18.  
  19.   sealed class DatabaseSwitcher : IDisposable {
  20.     Db.Database prewDb = null;
  21.     public DatabaseSwitcher(Db.Database db) {
  22.       prewDb = Hs.WorkingDatabase;
  23.       Hs.WorkingDatabase = db;
  24.     }
  25.     public void Dispose() {
  26.       Hs.WorkingDatabase = prewDb;
  27.     }
  28.   }
  29.  
  30.   sealed class DocumentSwitcher : IDisposable {
  31.     Ap.Document prewDoc = null;
  32.     Db.Database prewDb = null;
  33.     public DocumentSwitcher(Ap.Document doc) {
  34.       prewDoc = cad.DocumentManager.MdiActiveDocument;
  35.       prewDb = Hs.WorkingDatabase;
  36.       cad.DocumentManager.MdiActiveDocument = doc;
  37.       Hs.WorkingDatabase = doc.Database;
  38.     }
  39.     public void Dispose() {
  40.       cad.DocumentManager.MdiActiveDocument = prewDoc;
  41.       Hs.WorkingDatabase = prewDb;
  42.     }
  43.   }
  44.  
  45.  
  46.   [Mb.TestFixture]
  47.   [Mb.Description("The LayerTableRecord count testing.")]
  48.   sealed class LayerTableRecordTesting {
  49.     const String fileName = @"C:\public\acad_test\test.dwg";
  50.     Int32 count = 0;
  51.     [Mb.FixtureSetUp]
  52.     [Mb.Description("Create DWG file if it not exist still.")]
  53.     private void SetUp() {
  54.       if (!File.Exists(fileName)) {
  55.         using (Db.Database db = new Db.Database(true, true)) {
  56.           db.CloseInput(true);
  57.           using (DatabaseSwitcher dbsw = new DatabaseSwitcher(db)) {
  58.             using (Db.Transaction tr = db.TransactionManager.StartTransaction()
  59.               ) {
  60.               Db.LayerTable lt = tr.GetObject(db.LayerTableId,
  61.                 Db.OpenMode.ForWrite) as Db.LayerTable;
  62.               Db.LayerTableRecord record = new Db.LayerTableRecord();
  63.               record.Name = "Test-Layer";
  64.               record.Description = "This layer was created for testing";
  65.               lt.Add(record);
  66.               tr.AddNewlyCreatedDBObject(record, true);
  67.  
  68.               Db.LayerTableRecord record2 = new Db.LayerTableRecord();
  69.               record2.Name = "Test-Layer-2";
  70.               record2.Description = "This layer was created for testing too";
  71.               lt.Add(record2);
  72.               tr.AddNewlyCreatedDBObject(record2, true);
  73.  
  74.               Db.Circle circle = new Db.Circle();
  75.               circle.SetDatabaseDefaults();
  76.               circle.Radius = 100;
  77.               circle.Center = new Autodesk.AutoCAD.Geometry.Point3d(0, 0, 0);
  78.               circle.LayerId = record.ObjectId;
  79.               cad.SetSystemVariable("CLAYER", record.Name);
  80.  
  81.               Db.BlockTableRecord model = tr.GetObject(Us.GetBlockModelSpaceId(
  82.                 db),
  83.                 Db.OpenMode.ForWrite) as Db.BlockTableRecord;
  84.               model.AppendEntity(circle);
  85.               tr.AddNewlyCreatedDBObject(circle, true);
  86.  
  87.               IEnumerable<Db.ObjectId> layerIds = lt.Cast<Db.ObjectId>()
  88.                 .Where(n => n.IsValid && !n.IsErased && !n.IsEffectivelyErased
  89.                 );
  90.               count = layerIds.Count();
  91.               tr.Commit();
  92.             }
  93.           }
  94.           db.SaveAs(fileName, Db.DwgVersion.Newest);
  95.         }
  96.       }
  97.     }
  98.  
  99.     [Mb.Test]
  100.     [Mb.Description("LayerTableRecord iteration for a Database.")]
  101.     private void DatabaseTesting() {
  102.       Int32 countDb = 0;
  103.       using (Db.Database db = new Db.Database(true, true)) {
  104.         db.CloseInput(true);
  105.         using (DatabaseSwitcher dbsw = new DatabaseSwitcher(db)) {
  106.           db.ReadDwgFile(fileName, Db.FileOpenMode.OpenForReadAndReadShare,
  107.             false, String.Empty);
  108.           CheckLayerItems(db, ref countDb);
  109.         }
  110.       }
  111.       Mb.Assert.AreEqual<Int32>(count, countDb, "count != countDb.");
  112.     }
  113.  
  114.     [Mb.Test]
  115.     [Mb.Description("LayerTableRecord iteration for a Document.")]
  116.     private void DocumentTesting() {
  117.       Ap.Document doc = Ap.DocumentCollectionExtension.Add(cad.DocumentManager,
  118.         fileName);
  119.       Int32 countDwg = 0;
  120.       using (DocumentSwitcher docsw = new DocumentSwitcher(doc)) {
  121.         using (doc.LockDocument()) {
  122.           CheckLayerItems(doc.Database, ref countDwg);
  123.         }
  124.       }
  125.       Ap.DocumentExtension.CloseAndDiscard(doc); // I get an exception here.
  126.       Mb.Assert.AreEqual<Int32>(count, countDwg, "count != countDwg.");
  127.     }
  128.  
  129.     [Mb.FixtureTearDown]
  130.     void RemoveTestDwg() {
  131.       if (File.Exists(fileName))
  132.         File.Delete(fileName);
  133.     }
  134.  
  135.     private static void CheckLayerItems(Db.Database db, ref Int32 count) {
  136.       using (Db.Transaction tr = db.TransactionManager.StartTransaction()) {
  137.         Db.LayerTable lt = tr.GetObject(db.LayerTableId, Db.OpenMode.ForRead)
  138.           as Db.LayerTable;
  139.         Console.WriteLine("Layer names:");
  140.         IEnumerable<Db.ObjectId> layerIds = lt.Cast<Db.ObjectId>().Where(n =>
  141.           n.IsValid && !n.IsErased && !n.IsEffectivelyErased);
  142.         foreach (Db.ObjectId id in layerIds) {
  143.           Db.LayerTableRecord ltr = tr.GetObject(id, Db.OpenMode.ForRead)
  144.             as Db.LayerTableRecord;
  145.           Console.WriteLine(ltr.Name);
  146.         }
  147.         Console.WriteLine("Layers count: {0}.", count = layerIds.Count());
  148.         tr.Commit();
  149.       }
  150.     }
  151.   }
  152. }

But I get an exception (look the commented string):
Quote
[failed] Test acad_test/LayerTableRecordTesting/DocumentTesting
Execute
System.Runtime.InteropServices.COMException
ErrorCode: -2147467259
HelpLink: C:\Program Files\Autodesk\AutoCAD 2015\HELP\OLE_ERR.CHM
HResult: -2147467259
   at System.Runtime.InteropServices.Marshal.ThrowExceptionForHRInternal(Int32 errorCode, IntPtr errorInfo)
   at System.Runtime.InteropServices.Marshal.ThrowExceptionForHR(Int32 errorCode)
   at Autodesk.AutoCAD.ApplicationServices.DocumentExtension.CloseInternal(Document doc, Boolean discard, String fileName)
   at Autodesk.AutoCAD.ApplicationServices.DocumentExtension.CloseAndDiscard(Document doc)
   at Bushman.CAD.Testing.LayerTableRecordTesting.DocumentTesting() in d:\src\msvs\vs2013\tests\acad_test_solution\acad_test\LayerTableRecordTesting.cs:line 125

Why I get an exception?

This works fine (without my DocumentSwitcher):
Code - C#: [Select]
  1. private void DocumentTesting() {
  2.   Ap.Document doc = Ap.DocumentCollectionExtension.Add(cad.DocumentManager,
  3.     fileName);
  4.   Int32 countDwg = 0;
  5.   // using (DocumentSwitcher docsw = new DocumentSwitcher(doc)) {
  6.     using (doc.LockDocument()) {
  7.       CheckLayerItems(doc.Database, ref countDwg);
  8.     }
  9.   // }
  10.   Ap.DocumentExtension.CloseAndDiscard(doc); // This case it works fine.
  11.   Mb.Assert.AreEqual<Int32>(count, countDwg, "count != countDwg.");
  12. }

What is problem?

Locke

  • Guest
Re: Problem with active document and working database switching
« Reply #1 on: May 06, 2014, 02:48:40 PM »
Well... unless I'm missing something, DocumentExtension.CloseAndDiscard looks like your own code.  How is anyone supposed to know what's in it?  At first glance I'm guessing it has something to do with IDisposable not being implemented correctly.

At the risk of sounding like a huge jerk, do you mind me asking what the point of the DocumentSwitcher class is?

Andrey Bushman

  • Swamp Rat
  • Posts: 864
Re: Problem with active document and working database switching
« Reply #2 on: May 06, 2014, 02:52:50 PM »
Well... unless I'm missing something, DocumentExtension.CloseAndDiscard looks like your own code.
No, you are not right. It is the AutoCAD .NET API. The CloseAndDiscard is an extension method (i.e. static) in the newer AutoCAD versions.

Locke

  • Guest
Re: Problem with active document and working database switching
« Reply #3 on: May 06, 2014, 02:54:22 PM »
Which API is it for?  I only have up to 2012 on this machine.  I'll download the 2013/2014 APIs, I need to anyway.  I'll see if I can reproduce the issue when I get some time today.

Andrey Bushman

  • Swamp Rat
  • Posts: 864
Re: Problem with active document and working database switching
« Reply #4 on: May 06, 2014, 03:01:16 PM »
Which API is it for?  I only have up to 2012 on this machine.
I not remember and I can't look it in this time, because I write from the Linux in this time (from home). I can look this tomorrow (from office), if it is really interesting for you. I remember, AutoCAD 2014 use it too, but I not remember for the older AutoCAD versions (maybe 2013 too).
« Last Edit: May 06, 2014, 03:04:19 PM by Andrey Bushman »

Andrey Bushman

  • Swamp Rat
  • Posts: 864
Re: Problem with active document and working database switching
« Reply #5 on: May 06, 2014, 03:10:08 PM »
Quote
At the risk of sounding like a huge jerk, do you mind me asking what the point of the DocumentSwitcher class is?
I not understand your question. My English is very bad. Did you asked what you didn't understand the purpose of  DocumentSwitcher  class, or your question was about other?

Locke

  • Guest
Re: Problem with active document and working database switching
« Reply #6 on: May 06, 2014, 03:56:29 PM »
Quote
At the risk of sounding like a huge jerk, do you mind me asking what the point of the DocumentSwitcher class is?
I not understand your question. My English is very bad. Did you asked what you didn't understand the purpose of  DocumentSwitcher  class, or your question was about other?

Yes, I don't understand the purpose of DocumentSwitcher.  Can you explain the scenario it was designed for?

Andrey Bushman

  • Swamp Rat
  • Posts: 864
Re: Problem with active document and working database switching
« Reply #7 on: May 06, 2014, 04:04:08 PM »
Yes, I don't understand the purpose of DocumentSwitcher.  Can you explain the scenario it was designed for?
Instance of this class automatically change (for a while) the properties of MdiActiveDocument and the WorkingDatabase. Then it returns their previous values. Implementing of IDisposable guarantees this will occur even if unexpected exception occurs (if I use the "using").

Locke

  • Guest
Re: Problem with active document and working database switching
« Reply #8 on: May 06, 2014, 04:09:13 PM »
Yes, I don't understand the purpose of DocumentSwitcher.  Can you explain the scenario it was designed for?
Instance of this class automatically change (for a while) the properties of MdiActiveDocument and the WorkingDatabase. Then it returns their previous values. Implementing of IDisposable guarantees this will occur even if unexpected exception occurs (if I use the "using").

I understand the code, just not the scenario where this would be useful.  I'm not trying to be mean, just curious.

Andrey Bushman

  • Swamp Rat
  • Posts: 864
Re: Problem with active document and working database switching
« Reply #9 on: May 06, 2014, 04:19:45 PM »
This class allows me to be sure that at the moment, which are necessary to me, the WorkingDatabase property points to that object of Database which are corresponds to the MdiActiveDocument property. For example, when you open the Database via the "new Database(...)", then you must to set the WorkingDatabase manually (otherwise you may get the eWrongDatabase exception). In this case the WorkingDatabase will not correspond for the MdiActiveDocument. The DocumentSwitcher fix it for a while.

Jeff H

  • Needs a day job
  • Posts: 6150
Re: Problem with active document and working database switching
« Reply #10 on: May 06, 2014, 06:52:43 PM »
In 2015 you can not switch documents until  a command ends.

Andrey Bushman

  • Swamp Rat
  • Posts: 864
Re: Problem with active document and working database switching
« Reply #11 on: May 06, 2014, 10:45:30 PM »
In 2015 you can not switch documents until  a command ends.
How it belongs to a problem which I specified?