2. Проверка валидности ObjectId с помощью блока try/catch

    В этом разделе рассматривается создание класса, унаследованного от абстрактного класса DBSearcher и выполняющего проверку на валидность ObjectId посредством обработки исключения (Exception) в блоке try/catch. Проектируемому классу присвоим имя DBSearcher_TryCatch.

Класс DBSearcher_TryCatch

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
//Microsoft
using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.ComponentModel;
//AutoCAD
using acad = Autodesk.AutoCAD.ApplicationServices.Application;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
namespace Bushman.AutoCAD.Common {
/// <summary>
/// Класс, предназначенный для извлечения информации из базы данных чертежа и,
/// в случае необходимости, редактирования существующих в ней объектов.
/// Во всех методах класса из обработки ИСКЛЮЧАЮТСЯ объекты, значение
/// идентификатора которых равно ObjectId.Null, либо свойство IsValid
/// идентификатора равно false
/// </summary>
public sealed class DBSearcher_TryCatch : DBSearcher {
public DBSearcher_TryCatch(Database db) : base(db) { }
/// <summary>
/// Метод, с помощью которого определяется валидность ObjectId. В данной реализации
/// проверка определяется с помощью блока try/catch, в котором выполняется метод
/// Database.GetObjectId
/// </summary>
/// <param name="h">Проверяемый хэндл</param>
/// <param name="id">ссылка на ObjectId, в котором следует сохранить результат
/// в случае успешной проверки</param>
/// <returns>true - проверка прошла успешно, иначе - false</returns>
protected override bool IsValid(Handle h, ref ObjectId id) {
try {
id = TargetDb.GetObjectId(false, h, 0);
}
catch { return false; }
return id != ObjectId.Null && id.IsValid;
}
}
}

    Как видим - код класса весьма компактен в виду того, что основная его часть находится в базовом классе. Теперь давайте напишем код, с помощью которого мы протестируем на предмет скорости работу методов GetByTypes и GetAllObjects.

Тестирование производительности

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
//Microsoft
using System;
using System.Collections.Generic;
using System.Collections;
using System.Linq;
using System.Text;
using System.Diagnostics;
//AutoCAD
using acad = Autodesk.AutoCAD.ApplicationServices.Application;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
//[assembly: CommandClass(typeof(Bushman.AutoCAD.Common.DBSearcher_TryCatch_Test))]
namespace Bushman.AutoCAD.Common {
public sealed class DBSearcher_TryCatch_Test {
[CommandMethod("TryCatchTest1", CommandFlags.Modal)]
public void TryCatchTest1() {
Document dwg = acad.DocumentManager.MdiActiveDocument;
Database db = dwg.Database;
Editor ed = dwg.Editor;
//Создаём объект, с помощью которого будем выполнять итерацию по базе данных чертежа
IDBSearcher dbs = new DBSearcher_TryCatch(db);
long i = 0;
Stopwatch sw = System.Diagnostics.Stopwatch.StartNew();
//Получить идентификаторы всех объектов, сгруппированными по их классу
Dictionary<string, List<ObjectId>> grIds = dbs.GetByTypes((n, m) => ++i > 0);
sw.Stop();
//На консоль AutoCAD выводим информацию (упорядочив по алфавиту) о том, какого класса
//сколько объектов имеется в БД.
foreach (string item in grIds.Keys.OrderBy(n => n))
ed.WriteMessage(string.Format("Класс: {0};\tКол-во: {1}\n", item, grIds[item].Count));
ed.WriteMessage(string.Format("Всего объектов в БД: {0}\nВремя итерации: {1}\n", i, sw.Elapsed));
}
[CommandMethod("TryCatchTest2", CommandFlags.Modal)]
public void TryCatchTest2() {
Document dwg = acad.DocumentManager.MdiActiveDocument;
Database db = dwg.Database;
Editor ed = dwg.Editor;
//Создаём объект, с помощью которого будем выполнять итерацию по базе данных чертежа
IDBSearcher dbs = new DBSearcher_TryCatch(db);
Stopwatch sw = System.Diagnostics.Stopwatch.StartNew();
//Получить идентификаторы всех объектов базы данных чертежа
ObjectId[] ids = dbs.GetAllObjects(DBObjectStatus.NotErased);
sw.Stop();
ed.WriteMessage(string.Format("Всего объектов в БД: {0}\nВремя итерации: {1}\n", ids.Length, sw.Elapsed));
}
}
}

    Результат работы команд TryCatchTest1 и TryCatchTest2 будет следующим:

Command: TryCatchTest1
Класс: AcDb2dPolyline;  Кол-во: 9
Класс: AcDb2dVertex;       Кол-во: 1518
Класс: AcDb3dPolyline;  Кол-во: 10
Класс: AcDb3dPolylineVertex; Кол-во: 290
Класс: AcDbAlignedDimension; Кол-во: 50
Класс: AcDbArc;  Кол-во: 28331
Класс: AcDbBlockBegin;  Кол-во: 479
Класс: AcDbBlockEnd;       Кол-во: 479
Класс: AcDbBlockReference;      Кол-во: 2789
Класс: AcDbBlockTable;  Кол-во: 1
Класс: AcDbBlockTableRecord; Кол-во: 479
Класс: AcDbCellStyleMap;    Кол-во: 2
Класс: AcDbCircle;     Кол-во: 1857
Класс: AcDbDataTable; Кол-во: 1
Класс: AcDbDictionary;  Кол-во: 95
Класс: AcDbDictionaryVar;     Кол-во: 9
Класс: AcDbDictionaryWithDefault;      Кол-во: 1
Класс: AcDbDimAssoc;       Кол-во: 30
Класс: AcDbDimStyleTable;     Кол-во: 1
Класс: AcDbDimStyleTableRecord;    Кол-во: 5
Класс: AcDbEllipse;      Кол-во: 142
Класс: AcDbFontTable; Кол-во: 1
Класс: AcDbFontTableRecord;       Кол-во: 12
Класс: AcDbHatch;    Кол-во: 1611
Класс: AcDbLayerTable;  Кол-во: 1
Класс: AcDbLayerTableRecord; Кол-во: 66
Класс: AcDbLayout;     Кол-во: 5
Класс: AcDbLine;   Кол-во: 511892
Класс: AcDbLinetypeTable;     Кол-во: 1
Класс: AcDbLinetypeTableRecord;    Кол-во: 28
Класс: AcDbMaterial;       Кол-во: 3
Класс: AcDbMLeaderStyle;    Кол-во: 1
Класс: AcDbMlineStyle;  Кол-во: 1
Класс: AcDbMText;    Кол-во: 152
Класс: AcDbPlaceHolder;   Кол-во: 1
Класс: AcDbPoint;    Кол-во: 22000
Класс: AcDbPolyline;       Кол-во: 161900
Класс: AcDbRasterImage;   Кол-во: 12
Класс: AcDbRasterImageDef;      Кол-во: 2
Класс: AcDbRasterImageDefReactor;      Кол-во: 12
Класс: AcDbRasterVariables;       Кол-во: 1
Класс: AcDbRegAppTable;   Кол-во: 1
Класс: AcDbRegAppTableRecord;  Кол-во: 22
Класс: AcDbScale;    Кол-во: 595
Класс: AcDbSequenceEnd;   Кол-во: 19
Класс: AcDbSolid;    Кол-во: 372
Класс: AcDbSortentsTable;     Кол-во: 15
Класс: AcDbSpline;     Кол-во: 275
Класс: AcDbTableStyle;  Кол-во: 2
Класс: AcDbText;   Кол-во: 648
Класс: AcDbTextStyleTable;      Кол-во: 1
Класс: AcDbTextStyleTableRecord;     Кол-во: 13
Класс: AcDbUCSTable;       Кол-во: 1
Класс: AcDbViewport;       Кол-во: 6
Класс: AcDbViewportTable;     Кол-во: 1
Класс: AcDbViewportTableRecord;    Кол-во: 1
Класс: AcDbViewTable; Кол-во: 1
Класс: AcDbVisualStyle;   Кол-во: 16
Класс: AcDbVXTable;      Кол-во: 1
Класс: AcDbVXTableRecord;     Кол-во: 3
Класс: AcDbXrecord;      Кол-во: 41
Класс: AcDbZombieObject;    Кол-во: 9
Всего объектов в БД: 736323
Время итерации: 00:05:30.4050567

Command: TryCatchTest2
Всего объектов в БД: 736323
Время итерации: 00:05:36.1859781

    Т.о. в базе данных чертежа содержится 736 323 примитива, а на полную итерацию в первом тесте ушло 5 мин. 30 сек., а во втором - 5 мин. 36 сек. (т.е. разница не существенная).

    Столь долгая обработка чертежа обусловлена наличием исключений (Exceptions), которые возникают при попытке получения ObjectId на основе несуществующего значения Handle (в чертеже, на котором выполнялся данный код этих исключений набралось около 100 000 шт. - проверял в отладчике). Т.о. необходимо либо как-то выполнить проверку Handle на существование (чтобы не возникал Exception), либо найти иной способ получения идентификаторов всех объектов, имеющихся в базе данных чертежа. 

Итак, данный вариант реализации нас не устраивает. Напишем другую - с использованием P/Invoke, что позволит нам избавиться от блока try/catch...

Comments