Практическое занятие № 9
Тема: Работа с базами данных
Цель работы: получить практические навыки по созданию программ для работы с БД
Ход работы
Пусть в СУБД Access спроектирована база данных (скачать). База защищена паролем "rpo" и имеет две таблицы.
Таблица "students", состоит из полей:
|
Имя поля |
Тип |
Размер |
|
id_stud |
Счетчик |
Ключевое |
|
fam |
Текстовый |
15 |
|
imya |
Текстовый |
15 |
|
otch |
Текстовый |
15 |
|
finance |
Текстовый |
10 (Госзаказ, Контракт) |
|
datar |
Дата/время |
|
|
srbal |
Числовой |
Одинарной точности с плавающей точкой. Значение по умолчанию - 0 |
|
foto |
Поле OLE |
|
| id_grup | Числовой | Внешний ключ для связи с таблицей groups |
Таблица "groups", состоит из полей:
|
Имя поля |
Тип |
Размер |
| id_grup | Счетчик | Ключевое |
|
grupa |
Текстовый |
30 |
В программе на C# выполните подключение базы данных
Указания: на панели "Источники данных" щелкните на кнопке "Добавить новый источник данных" (если панель не отображается, то выбери команду меню "Данные - Показать источники данных"). Запуститься мастер создания подключения к БД. В окне выберите значение "База данных", затем значение "Набор данных". На следующем шаге щелкните на кнопке "Создать подключение". В новом окне Вам будет предложено подключить файлы БД SQL Server. Щелкните на кнопке "Изменить" и выберите формат БД "Microsoft Access". После этого укажите нужный файл БД (путь к файлу не удаляйте, среда C# самостоятельно скопирует указанный файл в папку проекта и скорректирует путь к нему). Так как база защищена паролем, то щелкните на кнопке "Дополнительно" и в строке "Jet OLEDB: DataBase Password" укажите пароль "rpo". Для проверки соединения щелкните на кнопке "Проверить подключение". После задания всех параметров мастер предложит выбрать способ сохранения пароля к БД: не включать в строку подключения или включать в строку подключения. Выберите первый вариант для безопасности.
После задания параметров БД при переходе на следующий шаг мастера появится запрос на копирование файла БД в папку с проектом, подтвердите эту операцию.
На следующем шаге мастер предложит сохранить строку подключения в файл конфигурации приложения. Так как пароль доступа не включен в строку подключения, то можете согласиться на сохранение в файл.
На следующем шаге выберите обе таблицы students и groups, с которыми планируете работать в проекте, и щелкните "Готово".
Сразу нужно настроить поведение полей-счетчиков в таблицах. В окне "Источники данных" щелкните на кнопке "Изменить набор данных в конструкторе". Отобразится структура таблиц. Выделите поле id_stud в таблице students и измените свойства: AutoIncrementSeed = 1, AutoIncrementStep = 1. Выделите поле id_grup в таблице groups и измените свойства: AutoIncrementSeed = 1, AutoIncrementStep = 1.
Настройка корректного удаления новых записей
Если добавить новую запись в таблицу и сразу попытаться ее удалить, то часто появляется сообщение об ошибке, связанное с тем, что мастер подключения БД неправильно формирует команды на добавление, обновление и удаление данных. Для решения этой проблемы в окне со структурой таблиц щелкните правой кнопкой на строке "studentsTableAdapter" таблицы students, выберите команду "Настроить". В новом окне щелкните на кнопке "Дополнительно", в следующем окне выключите флажок "Использовать оптимистическую блокировку", щелкните ОК и Готово.
Повторите эти действия для таблицы groups.
Создайте программу для работы с таблицей. При этом форма должна иметь вид:

Сетка заблокирована для ввода.
Указания: для подключения таблицы students нанесите на форму компонент DataRowView. Появится окно, в котором нужно указать подключаемую таблицу students. На форме появятся дополнительные компоненты для работы с таблицей: bazaDataSet - для доступа к подключению, studentsBindingSource - для работы с набором данных, studentsTableAdapter - для работы с копией таблицы БД. Для сетки задайте свойства: AutoSizeColumnsMode=Fill (ширина колонок подстраивается под размер сетки), AllowUserToAddRows = false (запрет добавления новых строк в сетке), ReadOnly=true.
Для подключения таблицы groups нанесите на форму компонент DataRowView. Появится окно, в котором нужно указать подключаемую таблицу groups. На форме появятся дополнительные компоненты для работы с таблицей: groupsBindingSource - для работы с набором данных, groupsTableAdapter - для работы с копией таблицы БД. Так как таблица groups не будет отображаться в сетке, а будет использоваться для выпадающего списка, то сетку удалите.
Если запустить программу на выполнение, то сетка будет пустой, потому что в строке соединения мы не сохранили пароль к БД и подключение не установлено.
Для формы откройте событие Load. В этом событии будут находится две команды Fill(), загружающие копии таблиц в компоненты TableAdapter. Над этими командами добавьте команду подстановки пароля в строку соединения:
Properties.Settings.Default["BazaConnectionString"] += ";Jet OLEDB:Database Password=rpo";
Добавьте в сетку поле Стипендия, которое вычисляется по формуле:
если финансирование равно "Госзаказ" и средний балл от 10..12 = 600;
если финансирование равно "Госзаказ" и средний балл 7..9 = 550
в остальных случаях 0.
Указания: для расчета будем пользоваться функцией вида:
iif(условие, значение при выполнение условия, значение при невыполнении условия)
В панели "Источники данных" щелкните на кнопке "Изменить набор данных в конструкторе".
В результате вы увидите структуры таблиц, входящих в подключение. В контекстном меню таблицы "students" выберите команду "Добавить - Столбец". Введите имя поля "stip" и в свойстве Expression введите формулу для расчета:
iif(finance='Госзаказ', iif(srbal=5,1450,iif(srbal>=4,1300,0)),0)
Перейдите на форму с сеткой. Вы увидите, что добавление новых колонок не приводит к обновлению данных. В сетке новое поле не появилось. Для решения проблемы обновите компонент studentsBindingSource. Для этого в его свойстве DataMember укажите значение "Отсутствует", а затем снова выберите таблицу "students".
В сетке отображаются поля (фамилия, имя, отчество, финансирование, средний балл). Все поля в сетке назвать по-русски. Сетка заблокирована для ввода.
Указания: для настройки параметров сетки откройте ее свойство Columns. Вы увидите список всех ее колонок. Для каждой колонки в свойстве HeaderText укажите название, в свойстве Visible можно скрыть ненужные колонки. Для сетки задайте свойство ReadOnly=true. Для работы нашего кода далее обязательно назовите поле со средним баллом - "Ср.балл"
Слева под сеткой разместите надпись Label для отображения номера текущей записи и общего числа записей.
Указания: разместите слева под сеткой компонент label1. При переходе по записям в сетке информация в этом компоненте должна изменяться. Для этого в событии CurrentChanged компонента studentsBindingSource введите код:
private void studentsBindingSource_CurrentChanged(object sender, EventArgs e)
{
label1.Text = (studentsBindingSource.Position + 1).ToString() + " из " + studentsBindingSource.Count.ToString();
}
На форме разместите две кнопки для добавления и удаления данных.
При этом с помощью кнопки "Добавить" можно ввести нового студента с помощью вспомогательной страничной формы. Кнопка "Удалить" удаляет текущую запись.
Двойной щелчок по строке в сетке открывает страничную форму для редактирования данных.
Указания: добавьте в проект новую форму Form2. Выключите на форме кнопку закрытия окна. Для этого задайте свойство ControlBox = false.
Перед добавление новой записи будем на всякий случай сохранять в переменной текущую позицию в наборе данных. Это нужно для того, что если пользователь откажется от сохранения новой записи, то указатель должен будет вернуться в исходную позицию. Для этого вверху главной формы Form1 опишите глобальную переменную:
public int pos=0;
Для кнопки "Добавить" на главной форме напишите код вида:
void Button1_Click(object sender, EventArgs e)
{
//запоминаем текущее положение в таблице
pos = studentsBindingSource.Position;
//добавляем новую запись
studentsBindingSource.AddNew();
//создаем экземпляр страничной формы
Form2 form2 = new Form2();
//открываем форму в модальном режиме
form2.ShowDialog();
}
Для кнопки Удалить напишем код:
void Button2_Click(object sender, EventArgs e)
{
//выдаем сообщение на подтверждение операции
if (MessageBox.Show("Удалить строку?",
"Подтвердите операцию",
MessageBoxButtons.YesNo,
MessageBoxIcon.Question,
MessageBoxDefaultButton.Button2)==DialogResult.Yes)
{
//если пользователь подтвердил операцию
//удаляем текущую строку
studentsBindingSource.RemoveCurrent();
//сохраняем изменения в БД
studentsTableAdapter.Update(bazaDataSet.students);
}
}
Двойным щелчком на сетке пользователь открывает страничную форму для редактирования текущей записи. Для этого в событии DoubleClick сетки напишем код:
private void dataGridView1_DoubleClick(object sender, EventArgs e)
{
//запоминаем текущее положение в таблице
pos = studentsBindingSource.Position;
//создаем экземпляр страничной формы
Form2 form2 = new Form2();
//открываем форму в модальном режиме
form2.ShowDialog();
}
Вспомогательная страничная форма имеет вид:

Фото сотрудника заполняется с помощью диалога выбора графического файла при щелчке на кнопке "+". Кнопка "-" очищает поле Фото.
Поле Группа заполняется с помощью выпадающего списка, формируемого на основе второй таблицы-справочника "groups".
Поле Финансирование заполняется с помощью выпадающего списка из двух значений (Госзаказ, Контракт).
Поле Дата рождения заполняется с помощью выпадающего календаря.
Кнопка Сохранить закрывает окно и сохраняет запись.
Кнопка Отменить закрывает окно и отменяет внесенные изменения.
Указания: разместите на второй форме 4 текстовых поля TextBox (фамилия, имя, отчество, средний балл) , 2 выпадающих списка ComboBox (финансирование и группа), календарь DateTimePicker (дата рождения). Для фото добавьте компонент PictureBox (задайте свойство SizeMode = Zoom) и две кнопки для добавления и очистки фото.
Фото будем заполнять с помощью диалога. Нанесите на форму компонент OpenFileDialog и в свойстве Filter укажите допустимые форматы графических файлов:
Графические файлы|*.jpg;*.jpeg;*.gif;*.bmp;*.png
Для кнопки "+" напишите код добавления фотографии:
void Button1_Click(object sender, EventArgs e)
{
//если файл в диалоге выбран
if (openFileDialog1.ShowDialog()==DialogResult.OK)
{
//записываем файл в переменную
Bitmap pict = new Bitmap(openFileDialog1.FileName);
//в поле pictureBox отображаем переменную
pictureBox1.Image = (Image)pict;
}
}
Для кнопки "-" напишем код очистки фотографии:
void Button2_Click(object sender, EventArgs e)
{
pictureBox1.Image=null;
}
Список Финансирование имеет два значения: Госзаказ, Контракт. Введите их через свойства Items списка.
Для поля Средний балл зададим ограничения от 3 до 5. При этом поле не может быть пустым и в поле можно вводить только числа. Что поле можно было пропустить, в нем нужно ввести 0.
Для поля textBox4 в событии Validating введем код:
private void textBox4_Validating(object sender, CancelEventArgs e)
{
//переменная для вызова метода TryParse
double rez;
//если поле равно 0, то код проверки не выполняется
if (textBox4.Text == "0") return;
//если содержимое поля не удалось конвертировать в double
//значит в поле не число
if (!double.TryParse(textBox4.Text,out rez))
{
//выдаем сообщение об ошибке
MessageBox.Show("Введенные данные не являются числом",
"Ошибка ввода",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
//отменяем ввод
e.Cancel=true;
//завершаем код проверки
return;
}
//если содержимое поля не попадает в диапазон от 3 до 5
if (Convert.ToDouble(textBox4.Text) < 3 || Convert.ToDouble(textBox4.Text) > 5)
{
//выдаем сообщение об ошибке
MessageBox.Show("Балл должен быть в диапазоне от 3 до 5",
"Ошибка ввода",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
//отменяем ввод
e.Cancel = true;
}
}
Все следующие действия будут производиться с помощью компонентов, размещенных на главной форме. По умолчанию главная форма не имеет имени и для обращения к ней нужно главной форме присвоить имя.
Откройте файл проекта Program.cs.
После строки заголовка класса проекта
static class Program
опишите глобальную переменную для доступа к главной форме в проекте
public static Form1 form1;
Замените команду
Application.Run(new Form1());
на команды
Program.form1 = new Form1();
Application.Run(form1);
После этого к главной форме проекта можно обратиться по имени Program.form1
Свяжем все компоненты на страничной форме Form2 с полями таблицы через компонент studentsBindingSource на главной форме (его имя будет Program.form1.studentsBindingSource). Сначала сделайте этот компонент видим для других форм. Для этого задайте свойство Modifiers = Public.
Поле справочник для ввода названия группы будет заполнять данными через компонент grousBindingSource (полное имя Program.form1.groupsBindingSource). Сделайте этот компонент также видимым для других форм (Modifiers = Public).
В событии Load страничной формы добавим код:
void Form2_Load(object sender, EventArgs e)
{
//поле textBox1 привязывается к полю fam
textBox1.DataBindings.Add(new Binding("Text", Program.form1.studentsBindingSource, "fam", true));
//поле textBox2 привязывается к полю imya
textBox2.DataBindings.Add(new Binding("Text", Program.form1.studentsBindingSource, "imya", true));
//поле textBox3 привязывается к полю otch
textBox3.DataBindings.Add(new Binding("Text", Program.form1.studentsBindingSource, "otch", true));
//список comboBox1 привязывается к полю finance
//так как список содержит фиксированный набор данных,
//то привязываем свойство списка Text
comboBox1.DataBindings.Add(new Binding("Text", Program.form1.studentsBindingSource, "finance", true));
//календарь dateTimePicker1 привязывается к полю datar
dateTimePicker1.DataBindings.Add(new Binding("Value", Program.form1.studentsBindingSource, "datar", true));
//поле textBox4.Text привязывается к полю srbal
textBox4.DataBindings.Add(new Binding("Text", Program.form1.studentsBindingSource, "srbal", true));
//список comboBox2 привязывается к полю id_grup
//так как список содержит набор данных из другой таблицы,
//то привязываем свойство списка SelectedValue
comboBox2.DataBindings.Add(new Binding("SelectedValue", Program.form1.studentsBindingSource, "id_grup", true));
//при этом значения для списка берутся из набора данных groupsBindingSource
//задаем параметры списка-справочника
//источник данных для списка
comboBox2.DataSource=Program.form1.groupsBindingSource;
//в списке отображаются названия групп
comboBox2.DataMember="grupa";
//после выбора в основную таблицу записывается номер группы
comboBox2.ValueMember="id_grup";
//компонент pictureBox1.Image привязывается к полю photo
pictureBox1.DataBindings.Add(new Binding("Image", Program.form1.studentsBindingSource, "foto", true));
}
Для сохранения или отмены мы будем обращаться к компонентам studentsTableAdapter и bazaDataSet на главной форме. Поэтому их нужно сделать "видимыми". Задайте для них свойство Modifiers = true.
Для кнопки Сохранить страничной формы напишем код:
void Button1_Click(object sender, EventArgs e)
{
//проверяем все поля на правильность ввода
Program.form1.Validate();
//завершаем редактирование текущей записи
Program.form1.bindingSource1.EndEdit();
//обновляем данные в БД
//при этом компоненты доступа к БД находятся на главной форме
Program.form1.studentsTableAdapter.Update(Program.form1.bazaDataSet.students);
//закрываем форму
Close();
}
Для кнопки Отменить напишем код:
void Button2_Click(object sender, EventArgs e)
{
//отменяем редактирование текущей записи
Program.form1.bindingSource1.CancelEdit();
//закрываем форму
Close();
//возвращаем курсор на предыдущую позицию в таблице
Program.form1.bindingSource1.Position=Program.form1.pos;
}
Справа от выпадающего списка "Группа" разместите кнопку для редактирования таблицы-справочника с помощью формы вида:

Указания: на форме Form1 для работы с таблицей-справочником сделайте видимым для других форм компонент groupsTableAdapter (Modifiers = Public).
На форме Form2 для кнопки справа от списка "Группа" введите код вида:
private void button5_Click(object sender, EventArgs e)
{
Form3 form3 = new Form3();
form3.ShowDialog();
}
Добавьте в проект новую форму Form3. Для формы задайте свойства: FormBorderSyle = FixedDialog, MaximizeBox = false, MinimizeBox = false, StartPosition = CenterScreen.
Список групп будет показывать в виде сетки. Нанесите на форму компонент DataRowView. Для сетки задайте свойство AutoSizeColumnsMode=Fill (ширина колонок подстраивается под размер сетки).
В событии Load формы привяжем сетку к источнику данных на главной форме.
private void Form3_Load(object sender, EventArgs e)
{
dataGridView1.DataSource = Program.form1.groupsBindingSource;
//скрываем первую колонку с полем-счетчиком
dataGridView1.Columns[0].Visible = false;
//переименовываем колонку с названием групп
dataGridView1.Columns[1].HeaderText = "Группа";
}
При закрытии формы сохраняем изменения в таблице-справочнике. В событии FormClosed напишем код:
private void Form3_FormClosed(object sender, FormClosedEventArgs e)
{
//если изменения в таблице есть
if (Program.form1.bazaDataSet.groups.GetChanges() != null)
//сохраняем их в БД
Program.form1.groupsTableAdapter.Update(Program.form1.bazaDataSet.groups);
}