Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ADO Intro 1 Task.docx
Скачиваний:
1
Добавлен:
01.05.2025
Размер:
555.2 Кб
Скачать

Канонизация имен студентов

В данный момент мы можем ввести двух студентов с именами: "Joe Doe" и " Joe Doe " (второе содержит массу лишних пробелов) и они будут считаться разными, хотя семантически они одинаковы. Для того, чтобы придать полю Name стандартный формат, давно напрашивается вставка метода Trim(string), который убирает пробелы не только в начале и конце имени, но и лишние пробелы между отдельными словами. Рассмотрим, как можно сделать это с помощью класса StringBuilder.

string Trim (string sOld)

{

StringBuilder sNew = new StringBuilder();

bool wasSpace = true;

for (int i=0; i<sOld.Length; i++)

{

bool isSpace = sOld[i] == ' ';

if (!(wasSpace && isSpace))

sNew.Append (sOld[i]);

wasSpace = isSpace;

}

return sNew.ToString().TrimEnd();

}

В методе Trim используется объект класса StringBuilder. Он позволяет работать со строкой текста, как с коллекцией символов. Сначала коллекция пуста, затем в цикле прохода по старой строке мы добавляем в нее только те символы, которые считаем нужными. Здесь работает метод Append, который меет 18 перегруженных версий и, поэтому, позволяет очень гибко работать с вновь генерируемой строкой. Обязательно просмотрите справку по этому методу.

Задействуйте процесс унификации имен, добавив в начало обработчика события RowChanged следующий код:

ds.Tables[0].RowChanged -= OnStudsRowChanged;

e.Row["Name"] = Trim(e.Row["Name"].ToString());

ds.Tables[0].RowChanged += OnStudsRowChanged;

На лекции мы осуждали необходимость выключения (см. операции –= и +=) и повторного включения задания делегата, реагирующего на изменения в строке таблицы студентов. Что будет, если пренебречь этим?

Временное выключение делегата необходимо, чтобы не получить бесконечный цикл обработки события RowChanged, так как присвоение e.Row["Name"] вновь генерирует событие RowChanged. Теперь коррекция имени будет производиться не только при добавлении новой строки, но и при изменении существующей. Проверьте это и объясните.

Вспомнив о возможностях класса Regex, разработаем новую версию метода Trim, которая состоит из одной строки кода. Замените существующую версию на новую.

string Trim(string s) { return new Regex(@"\s{2,}").Replace(s.Trim(), " "); }

При создании объекта класса Regex мы задаем шаблон регулярного выражения ("\s{2,}"). Мета-символ \s означает space (пустые символы). Шаблон говорит классу Regex, что он должен искать подстроки, которые содержат 2 или более пустых символов. Метод Replace класса Regex осуществляет замену всех найденных в строке s вхождений подстроки, определенной шаблоном, на строку " ", которая содержит только один пробел. Строка s предварительно обрабатывается методом Trim() класса string, что необходимо для уничтожения крайних пробелов.

Еще лучшим решением рассматриваемой проблемы будет добавление в проект класса Helper с методом GetName, который выуживает имя из строки символов с помощью механизма регулярных выражений. Мы использовали такой метод в предыдущей части курса.

Вычисляемые колонки DataTable (Expression-Based DataColumn Objects)

В множество колонок объекта DataTable легко добавить вычисляемые колонки, то есть, поля данных, которых нет в источнике данных, но они вычисляются и добавляются в таблицу на ходу. Это, в частности, можно сделать с помощью объекта DataRelation. Каждый студент сдал какие-то экзамены и они хранятся в связанной таблице Exams. Их количество может отличаться (если нет, то вы сделаете его разным, работая с формой и XML-файлом данных). С помощью объекта DataRelation мы вычислим количество экзаменов для каждого студента и покажем его в отдельной колонке таблицы DataTable, а, следовательно, (благодаря механизму DataBinding) и DataGridView. Добавим в таблицу студентов колонку, отображающую количество сданных ими экзаменов. Это делается удивительно просто.

ds.Tables[0].Columns.Add("ExamsNo", typeof(int), "Count(Child.StudID)");

Эту строку кода следует вставить после того, как в DataSet была добавлена связь между таблицами студентов и экзаменов, но до вызова метода AddStyle (иначе наш стиль не подействует на новую колонку). Секрет ее работы состоит в строковой константе "Count(Child.StudID)" (вспомните про роль имен в механизме DataBinding). Она определяет свойство Expression (вычисляемое выражение), которое имеется в каждом объекте класса DataColumn. Рассмотрим части выражения.

  • Count() определяет функцию SQL-запроса,

  • Child определяет множество строк, таблицы Exams, связанных с текущей строкой таблицы студентов,

  • StudID определяет поле внешнего ключа связи (FK).

Обратите внимание на то, что DataGridView не позволяет редактировать поля данных этой колонки и это правильно. Но, вот недостаток! Если вы теперь вызовите метод Save, то данные вычисляемой колонки попадут в XML-файл, что не правильно. Предотвратить это можно, взяв в свои руки процесс записи в файл. Одним из способов может быть удаление колонки перед записью в файл и вставка ее после чтения из файла. Другим способом может быть работа с методами класса XmlDocument. Вместо вызова методов WriteXml класса DataSet можно вызвать метод Serialize класса XmlSerializer, но для этого придется ввести промежуточный слой: сериализуемые классы или класс, производный от DataTable. В промежуточном слое надо пометить исключаемое свойство атрибутом [XmlIgnore], или не иметь его вовсе.

Оказалось, что для решения этой проблемы есть простой способ — установить свойство ColumnMapping для вычисляемой колонки в значение Hidden. Данные такой колонки не попадают в XML-файл.

  • Добавьте следующую установку в метод RelateAndBind после привязки DataGridView к данным таблиц.

ds.Tables[0].Columns[4].ColumnMapping = MappingType.Hidden;

  • Добавьте колонку, которая вычисляет средний балл студента и для нее также установите свойство ColumnMapping. Тип данных для этой колонки не должен быть целочисленным.

Форматирование данных колонки производится в одной из ветвей метода AddStyle (определите ее самостоятельно). Для форматирования использован такой код:

col.DefaultCellStyle.Format = "f2";

Если вы добавили на форму BindingNavigator, то целесообразно включить его в работу таким изящным способом.

gridStud.CellEnter += (s, e) => bn.BindingSource = bs[0];

gridExam.CellEnter += (s, e) => bn.BindingSource = bs[1];

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]