Сегодняшний выпуск я начну с извинений, приведший к шквалу справедливого недовольства в мой адрес. Все размеры указанные для книг в предыдущем выпуске следует считать Мегабайтами, а не Гигабайтами...
Прошу у всех прощения за досадную ошибку, заработался.
В данной статье вы встретите ряд примеров, которые помогут вам на практике закрепить полученные при ее чтении навыки.
Поскольку в установленном мною недавно Microsoft Office 2003 почему-то не оказалось базы данных «Борей», которую я использовал до сих пор для примеров, с этого момента и далее я буду использовать с этой целью аналогичную базу NorthWind, которую я обнаружил в составе Visual Studio 6.
Для выполнения примеров вам придется выполнить некоторые подготовительные действия: открыть проект (или создать новый) на языке C#, создать соединение с базой данных NorthWind и задать ему имя cnnNwind.
Если при этом у вас возникнут затруднения, рекомендую перечитать предыдущую статью курса, в которой установка соединения с источником данных рассмотрена подробно.
Соглашения о терминах
Внимательный читатель, наверное, обратит внимание на то, что термин «объект» в данном цикле порой трактуется несколько вольно.
Так, например, при чтении вы неоднократно встретите выражения вроде «объект Command». Разумеется, речь идет при этом отнюдь не об объекте с точно таким именем.
Во-первых, очевидно, что речь при этом идет об экземпляре объекта, относящегося к данному классу.
Во-вторых, как вы уже знаете из предыдущих статей, реализации многих классов ADO.NET различаются в зависимости от используемого программой Провайдера Данных. Так, например, не существует класса Command в «чистом» виде. Вместо него следует использовать для Провайдера OLE DB – OleDbCommand, для Провайдера MS SQL Server – соответственно SqlCommand.
Тем не менее, реализации этих классов весьма сходны, а отличия в деталях я по возможности оговариваю отдельно. Таким образом, если речь идет об «объекте Command», не забывайте, что на самом деле имеются в виду «общие черты экземпляров классов OleDbCommand и SqlCommand». Впрочем, сомневаюсь, что столь строгое следование терминологии сделало бы статью легче читаемой.
По мере появления новых Провайдеров Данных в составе ADO.NET появятся новые классы, реализующие абстракцию Command. Впрочем, следует ожидать, что большая часть сказанного в этой статье будет в полной мере относиться и к ним.
Итак, надеюсь, что небольшие вольности с трактовкой понятия объекта в данном цикле не введут читателя в заблуждение.
В этой статье я хочу предложить класс CStackDoUndo, предназначенный для выполнения отката (а точнее - отката и возврата т.е. операций Undo и Do). Сохраняемые объекты могут быть любыми - от простых переменных и массивов до описаний действий, произведённых над данными. Следовательно, если непосредственно перед изменениями сохранить данные или сохранить описание действий, то откат станет возможен - просто надо "проиграть" всё в обратном порядке или заменить содержимое переменных. Стеки UNDO и DO можно представить как две трубы, линии осей которых совпадают, а между торцами этимх труб имеется некоторое расстояние. Труба слева - Это стек UNDO, справа - DO, а просвет - это текущее состояние (ТС). UNDO-ТС-DO Сначала трубы пусты. Когда ТС требуется изменить, оно вдвигается в стек UNDO, а затем изменяется. Если UNDO становится полностью заполнен, то элемент, вдвинутый в UNDO первым
(он расположен самым левым в UNDO) "выпадает" и теряется при помещении в UNDO нового элемента. Заметьте, что при этом стек DO нужно очистить, так как его содержимое практически теряет смысл. При команде "undo" если стек UNDO не пуст, то ТС помещается в стек DO, а из UNDO извлекается последний помещённый элемент и помещается на место ТС. Если стек UNDO пуст, ничего не произойдёт. Команда "do" выполняются наоборот: DO->TC->UNDO.
Так как для правильной работы класса необходимо сначала описать сохраняемые данные , например в виде структуры, то для каждого типа такой структуры класс нужно будет немного подогнать под. Собственно, сохраняемые даные описываются в структуре item класса и только его надо будет менять. Вернее: описание указателей, содержимое функции extract() и содержимое конструктора и деструктора item() и ~item(). Вот заголовочный файл класса:
//
файл StackDoUndo.h class CStackDoUndo { public: /////////////////// интерфейс класса/////////////////////////// //эта процедура вызывается перед изменениями void SaveBeforeChange(item *ITEM); void OperationDo(item *ITEM); //выполнить действие "Do" void OperationUndo(item *ITEM); //выполнить действие "UnDo" void ClearDoUndoStacks();//Очистить оба стека int GetDoLen();//получить длину стека Do int GetUndoLen();//получить длину стека Undo ////////////////////////////////////////////////////////////////////////// CStackDoUndo(int UndoStackSize=5,int DoStackSize=5);//конструктор ~CStackDoUndo(); //деструктор struct item; //структура "элемент" для DO/UNDO private: int SP_Undo; //счётчик элементов стека UNDO int SP_Do; //счётчик элементов стека DO int StackSize_Undo; //размер стека UNDO. item **Stack_Undo; //счётчик
элементов стека UNDO int StackSize_Do; //размер стека DO. item **Stack_Do; //счётчик элементов стека DO
void PushToDo(item *ITEM); //добавить в стек Do bool PopFromDo(item *ITEM); //извлечь из стека Do void PushToUndo(item *ITEM); //добавить в стек UnDo bool PopFromUndo(item *ITEM); //извлечь из стека UnDo void ClearDoStack();//очистка стека DO void ClearUndoStack();//очистка стека UNDO public: //структура "элемент" для DO/UNDO //(описывает сохраняемые данные) struct item { bool m_bMadeEmpty;//Способ заполнения элемента bool m_being;//флаг существования "элемента"
//Далее - всё зависит от типов сохраняемых данных
//Объявление УКАЗАТЕЛЕЙ для переменных и массивов int *m_pH, *m_pW;//размер матрицы(height, width) BYTE *m_pMatrix;//для массива, куда скопируется матрица.
//конструктор. Сюда передаются
УКАЗАТЕЛИ для //переменных и массивов внешних данных item(BYTE *Matrix, int *H, int *W, bool bToMakeEmpty=true);
//извлечь данные из "элемента" void extract(item *ITEM);
//деструктор. Здесь удаляются динамически созданные //переменные и массивы ~item(); }; };
Переопределить item для других типов данных несложно, далее будет рассказано, как это сделать. Для примера в данном коде item настроен на сохранение матрицы типа BYTE размером H*W , где H - высота, W-ширина. В структуре item определены члены int m_H, m_W для хранения ширины и высоты и BYTE *m_Matrix для хранения указателя на массив, который будет создан для копирования сохраняемого массива (либо уже существует - об это далее).