1. Абстрактный класс DBSearcher

    В этом разделе будет написан класс DBSearcher, предназначенный для решения следующих задач:
  1. Реализация интерфейса IDBSearcher
  2. Инкапсуляция кода, общего для всех дочерних классов
Итак, пишем код.

Класс DBSearcher

  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
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
//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 abstract class DBSearcher : IDBSearcher {
Database db;
/// <summary>
/// Чертёж, из которого извлекаются объекты
/// </summary>
public Database TargetDb {
get { return db; }
private set { db = value; }
}
/// <summary>
/// По умолчанию источником данных устанавливается текущий чертёж
/// </summary>
/// <param name="targetDb">База данных чертежа, в которой следует
/// выполнять поиск</param>
public DBSearcher(Database targetDb) {
TargetDb = targetDb;
}
/// <summary>
/// Абстрактный метод, с помощью которого определяется валидность ObjectId.
/// Поскольку метод абстрактный - он обязательно должен переопределяться в
/// наследуемых классах
/// </summary>
/// <param name="h">Проверяемый хэндл</param>
/// <param name="id">ссылка на ObjectId, в котором следует сохранить результат
/// в случае успешной проверки</param>
/// <returns>true - проверка прошла успешно, иначе - false</returns>
protected abstract bool IsValid(Handle h, ref ObjectId id);
/// <summary>
/// Получить идентификаторы всех объектов, унаследованных от DBObject, имеющихся в базе
/// данных чертежа
/// </summary>
/// <param name="status">Какие именно объекты (удалённые/не удалённые/все) следует выбирать</param>
/// <returns>Возвращается массив ObjectId[]</returns>
public ObjectId[] GetAllObjects(DBObjectStatus status) {
bool? a = null;
switch (status) {
case DBObjectStatus.Any:
a = null;
break;
case DBObjectStatus.Erased:
a = true;
break;
case DBObjectStatus.NotErased:
a = false;
break;
}
return GetData<ObjectId>((n, x) => !a.HasValue ? true : x.IsErased == (bool) a, (n, m) => m);
}
/// <summary>
/// Выбрать данные из базы данных
/// </summary>
/// <typeparam name="R">Тип объектов, возвращаемых в массиве</typeparam>
/// <param name="requirement">Условие выборки объектов</param>
/// <param name="result">Правило построения результирующего объекта</param>
/// <returns>Возвращается массив объектов R</returns>
public R[] GetData<R>(Func<Transaction, ObjectId, bool> requirement, Func<Transaction, ObjectId, R> result) {
List<R> primitives = new List<R>();
using (Transaction t = TargetDb.TransactionManager.StartTransaction()) {
for (long i = TargetDb.BlockTableId.Handle.Value; i < TargetDb.Handseed.Value; i++) {
ObjectId id = ObjectId.Null;
Handle h = new Handle(i);
if (IsValid(h, ref id) && requirement(t, id))
primitives.Add(result(t, id));
}
}
return primitives.ToArray();
}
/// <summary>
/// Получить словарь идентификаторов объектов, соответствующих некоторому условию.
/// Результирующая выборка будет представлена в виде словаря и сгруппирована в списки
/// по именам классов (берётся из ObjectId.ObjectClass.Name), которые в свою очередь
/// выступают в роли ключа.
/// Т.о. можно быстро выбрать все объекты нужного типа
/// </summary>
/// <param name="requirement">Условие выборки объектов</param>
/// <returns>Возвращается словарь, у которого в качестве ключа используется имя класса
/// (берётся из ObjectId.ObjectClass.Name), а в качестве значения - список, содержащий
/// в себе идентификаторы объектов этого класса</returns>
public Dictionary<string, List<ObjectId>> GetByTypes(Func<Transaction, ObjectId, bool> requirement) {
Dictionary<string, List<ObjectId>> dict = new Dictionary<string, List<ObjectId>>();
using (Transaction t = TargetDb.TransactionManager.StartTransaction()) {
for (long i = TargetDb.BlockTableId.Handle.Value; i < TargetDb.Handseed.Value; i++) {
ObjectId id = ObjectId.Null;
Handle h = new Handle(i);
if (IsValid(h, ref id) && requirement(t, id)) {
string type = id.ObjectClass.Name;
if (!dict.Keys.Contains(type))
dict.Add(type, new List<ObjectId>());
dict[type].Add(id);
}
}
}
return dict;
}
/// <summary>
/// Сгруппировать идентификаторы объектов по их классам
/// </summary>
/// <param name="ids">Исходный массив данных</param>
/// <returns>Возвращается словарь, у которого в качестве ключа используется имя класса
/// (берётся из ObjectId.ObjectClass.Name), а в качестве значения - список, содержащий
/// в себе идентификаторы объектов этого класса</returns>
public Dictionary<string, List<ObjectId>> GroupByTypes(ObjectId[] ids) {
Dictionary<string, List<ObjectId>> dict = new Dictionary<string, List<ObjectId>>();
using (Transaction t = TargetDb.TransactionManager.StartTransaction()) {
foreach (ObjectId id in ids) {
string type = id.ObjectClass.Name;
if (!dict.Keys.Contains(type))
dict.Add(type, new List<ObjectId>());
dict[type].Add(id);
}
}
return dict;
}
/// <summary>
/// Выполнить какое-то действие над некоторым набором примитивов
/// </summary>
/// <param name="ids">Идентификаторы объектов, подлежащих модификации</param>
/// <param name="action">Действие, которое необходимо выполнить над каждым из указанных объектов</param>
public void Action(ObjectId[] ids, Action<Transaction, ObjectId> action) {
using (Transaction t = TargetDb.TransactionManager.StartTransaction()) {
foreach (ObjectId item in ids)
action(t, item);
t.Commit();
}
}
}
}

    Обратите внимание на метод IsValid - он не является частью интерфейса IDBSearcher и кроме того - является абстрактным. Это означает, что он в обязательном порядке должен быть переопределён в наследуемых классах. Т.о. в классах, унаследованных от DBSearcher нам потребуется лишь реализовать конструктор класса (эта реализация сводится к банальному вызову конструктора родительского класса), а так же переопределить метод IsValid. Более ничего делать не придётся. Класс DBSearcher создан абстрактным, т.к. в нём не реализован метод IsValid - эта реализация должна быть выполнена в наследуемых классах.

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

Comments