Практическое занятие № 13

Тема: Обработка данных в отношении многие-ко-многим

Цель работы: получить практические навыки по настройке подключения к БД и обработке таблиц, связанных отношением многие-ко-многим

 

Ход работы

 

Пусть имеется БД catalog для хранения данных о книгах, читателях и учете выдачи и возврата книг. БД имеет три таблицы, связанные отношением многие-ко-многим.

Необходимо подключить БД к проекту. При этом путь к БД должен храниться в INI файле.

 

Указания: на панели "Источники данных" щелкните на кнопке "Добавить новый источник данных" (если панель не отображается, то выбери команду меню "Данные - Показать источники данных"). Запуститься мастер создания подключения к БД. В окне выберите значение "База данных", затем значение "Набор данных". На следующем шаге щелкните на кнопке "Создать подключение". В новом окне Вам будет предложено подключить файлы БД SQL Server. Щелкните на кнопке "Изменить" и выберите формат БД "Microsoft Access". После этого укажите нужный файл БД (путь к файлу не удаляйте, среда C# самостоятельно скопирует указанный файл в папку проекта и скорректирует путь к нему). Так как база защищена паролем, то щелкните на кнопке "Дополнительно" и в строке "Jet OLEDB: DataBase Password" укажите пароль "rpo". Для проверки соединения щелкните на кнопке "Проверить подключение". После задания всех параметров мастер предложит выбрать способ сохранения пароля к БД: не включать в строку подключения или включать в строку подключения. Выберите первый вариант для безопасности.

После задания параметров БД при переходе на следующий шаг мастера появится запрос на копирование файла БД в папку с проектом, подтвердите эту операцию.

На следующем шаге мастер предложит сохранить строку подключения в файл конфигурации приложения. Так как пароль доступа не включен в строку подключения, то можете согласиться на сохранение в файл.

На следующем шаге выберите все таблицы и щелкните "Готово".

Сразу нужно настроить поведение полей-счетчиков в таблицахВ окне "Источники данных" щелкните на кнопке "Изменить набор данных в конструкторе". Отобразится структура таблиц. Выделите поле id_book в таблице books и измените свойства: AutoIncrementSeed = 1AutoIncrementStep = 1Выделите поле id_reader в таблице readers и измените свойства: AutoIncrementSeed = 1, AutoIncrementStep = 1. Выделите поле id_uchet в таблице uchet и измените свойства: AutoIncrementSeed = 1, AutoIncrementStep = 1. 

 

Отобразите на форме данные из таблиц как показано на рисунке.

 

 

Указания:  для формы задайте свойства: Caption=Учет выдачи книг, Position=DesktopCenter, Borderstyle=bsSingle (нерастягиваемая форма), MiximizeBox = false (нет кнопки разворачивания окна).

 

Для подключения таблицы books нанесите на форму компонент DataRowView. Появится окно, в котором нужно указать подключаемую таблицу booksНа форме появятся дополнительные компоненты для работы с таблицей: catalogDataSet - для доступа к подключению, booksBindingSource - для работы с набором данных, booksTableAdapter - для работы с копией таблицы БД. Названия книг будут отображать в алфавитном порядке. Для этого для компонента booksBindingSource задайте свойство Sort = nazv_book (имя поля для сортировки). Для сетки задайте свойства: AllowUserToAddRows = false (запрет добавления новых строк в сетке), AllowUserToDeleteRows = false (запрет на удаление данных в сетке без подтверждения), AutoSizeColumnsMode=Fill (ширина колонок подстраивается под размер сетки)Для настройки параметров колонок сетки откройте ее свойство ColumnsВы увидите список всех ее колонок. Колонки id_book и label сделайте невидимыми (Visible=false), для остальных колонок в свойстве HeaderText укажите названия как на рисунке, в свойстве Width задайте для колонок подходящую ширину.

 

Для подключения таблицы readers нанесите на форму компонент DataRowView. Появится окно, в котором нужно указать подключаемую таблицу readersНа форме появятся дополнительные компоненты для работы с таблицей: readersBindingSource - для работы с набором данных, readersTableAdapter - для работы с копией таблицы БД. Фамилии читателей будут отображать в алфавитном порядке. Для этого для компонента readersBindingSource задайте свойство Sort = fam (имя поля для сортировки). Для сетки задайте свойства: AllowUserToAddRows = false (запрет добавления новых строк в сетке), AllowUserToDeleteRows = false (запрет на удаление данных в сетке без подтверждения), AutoSizeColumnsMode=Fill (ширина колонок подстраивается под размер сетки)Для настройки параметров колонок сетки откройте ее свойство ColumnsВы увидите список всех ее колонок. Колонку id_reader сделайте невидимой (Visible=false), для остальных колонок в свойстве HeaderText укажите названия как на рисунке.

 

Для отображения данных об учете выдачи книг создадим связанный набор данных по таблицам readers и uchet. В панели "Источники данных" щелкните на кнопке "Изменить набор данных в конструкторе". Щелкните правой кнопкой на фоне окна со структурами таблиц и выберите команду "Добавить - Table Adapter". В новом окне введите текст запроса:

 

//отбираем данные учета выдачи для текущей книги

//номер книги задается через параметр запроса

SELECT id_uchet, fam, imya, otch, data_in, data_out

FROM uchet INNER JOIN readers

ON uchet.id_reader = readers.id_reader

where id_book=?

 

В результате будет создана виртуальная таблица с методом заполнения Fiil(id_book). При вызове этого метода нужно будет в качестве параметра подставлять id_book нужной книги.

Переименуйте созданную виртуальную таблицу под именем BookUchet. Для ее подключения нанесите на форму компонент DataRowView. Появится окно, в котором нужно указать подключаемую таблицу BookUchetНа форме появятся дополнительные компоненты для работы с таблицей: bookUchetBindingSource - для работы с набором данных, bookUchetTableAdapter - для работы с копией таблицы БД. Данные будем отображать в хроническом порядке выдачи книг. Для этого для компонента  bookUchetBindingSource задайте свойство Sort = data_out Desc (сортировка по дате выдачи по убыванию). Для сетки задайте свойства: AllowUserToAddRows = false (запрет добавления новых строк в сетке), AllowUserToDeleteRows = false (запрет на удаление данных в сетке без подтверждения), AutoSizeColumnsMode=Fill (ширина колонок подстраивается под размер сетки)Для настройки параметров колонок сетки откройте ее свойство ColumnsВы увидите список всех ее колонок. Колонку id_uchet сделайте невидимой (Visible=false), для остальных колонок в свойстве HeaderText укажите названия как на рисунке.

 

Если запустить программу на выполнение, то сетки будут пустыми, потому что в строке соединения мы не сохранили пароль к БД и подключение не установлено.

Для формы откройте событие LoadВ этом событии будут находится команды Fill() для таблиц books и readers, загружающие копии таблиц в компоненты TableAdapterНад этими командами добавьте команду подстановки пароля в строку соединения:

 

Properties.Settings.Default["catalogConnectionString"] += ";Jet OLEDB:Database Password=rpo";

 

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

 

Указания: для набора данных booksBindingSource в событии CurrentChanged введите код:

 

 private void booksBindingSource_CurrentChanged(object sender, EventArgs e)

{

    //заполняем набор данных в соответствии в номером текущей книги   

    bookUchetTableAdapter.Fill(catalogDataSet.BookUchet,

                               Convert.ToInt32((booksBindingSource.Current as DataRowView)["id_book"]));

}

 

В верхней сетке будет отмечать цветом выданные книги. В таблицы имеется поле label. Если label = 1 (книга выдана), то строка закрашивается цветом. Если label = 0 (книга возвращена), то цвет возвращается в исходный.

 

Указания: в сетке dataGridView1 в событии RowPrePaint введите код:

 

private void dataGridView1_RowPrePaint(object sender, DataGridViewRowPrePaintEventArgs e)

{

    //описываем переменную для сохранения исходного цвета

    Color c = new Color();

    //сохраняем исходный цвет сетки

    c = dataGridView1.DefaultCellStyle.BackColor;

    //если 7-я колонка (соответствует полю "label") равна 0 (книга возвращена)

    if (dataGridView1.Rows[e.RowIndex].Cells[6].Value.ToString() == "0")

        //текущую строку закрашиваем исходным цветом

        dataGridView1.Rows[e.RowIndex].DefaultCellStyle.BackColor = c;

    //если 7-я колонка (соответствует полю "label") равна 1 (книга выдана)

    else if (dataGridView1.Rows[e.RowIndex].Cells[6].Value.ToString()=="1")

        //текущую строку закрашиваем светло-оранжево-розовым цветом

        dataGridView1.Rows[e.RowIndex].DefaultCellStyle.BackColor = Color.LightSalmon;

}

 

Добавьте на форму два текстовых поля. Первое поле предназначено для ввода и поиска книги по ее шифру. Второе поле предназначено для ввода и поиска читателя по его шифру.

 

Указания: нанесите на форму компонент TextBox и в событии TextChanged введите код:

 

private void textBox1_TextChanged(object sender, EventArgs e)

{

    //в наборе данных по таблице books находим номер записи,

    //у которой шифр равен значению в первом текстовом поле (метод Find())

    //затем устанавливаем указатель в наборе данных на найденный номер записи

    booksBindingSource.Position=booksBindingSource.Find("shifr",textBox1.Text);

}

 

Нанесите на форму компонент TextBox и в событии TextChanged введите код:

 

private void textBox2_TextChanged(object sender, EventArgs e)

{

    //в наборе данных по таблице readers находим номер записи,

    //у которой шифр равен значению во втором текстовом поле (метод Find())

    //затем устанавливаем указатель в наборе данных на найденный номер записи

    readersBindingSource.Position=readersBindingSource.Find("shifr",textBox2.Text);

}

 

Реализуйте функцию учета выдачи книги. При этом, если для книги label = 1 (книга выдана), то операция не выполняется.

 

Указания: набор данных BookUchet построен на основе запроса по двум таблицам. Это делаем невозможным стандартное добавление записей (методом AddNew()).

Для решения проблемы добавление будем выполнять с помощью запроса INSERT.

В панели "Источники данных" щелкните на кнопке "Изменить набор данных в конструкторе". В контекстном меню таблицы BookUchet выберите команду "Добавить - Запрос", в появившемся окне выберите тип запроса INSERT и в следующем окне введите текст запроса:

 

//в новой записи заполняем код книги, код читателя и дату выдачи

INSERT INTO uchet

(id_book, id_reader, data_out)

VALUES (?, ?, ?)

 

На следующем шаге в качестве имени метода укажите имя AddVydacha. В результате будет создан новый метод с параметрами AddVydacha(id_book, id_reader, data_out).

Нанесите на форму первую кнопку и для кнопки введите код:

 

private void button1_Click(object sender, EventArgs e)

{

    //если в таблице books поле label = 1 (книга выдана)

    if ((booksBindingSource.Current as DataRowView)["label"].ToString() == "1")

    {

        //выдаем сообщение об ошибке

        MessageBox.Show("Указанная книга уже выдана",

                        "Операция отменена",

                        MessageBoxButtons.OK,

                        MessageBoxIcon.Error);

        //завершаем работу подпрограммы

        return;

    }

       //для записи данных о выдаче книги в таблицу uchet

    //вызываем метод AddVydacha и передаем в него параметры

    //id_book из первой таблицы,

    //id_reader из второй таблицы и текущую дату

    bookUchetTableAdapter.AddVydacha(

        Convert.ToInt32((booksBindingSource.Current as DataRowView)["id_book"]),

        Convert.ToInt32((readersBindingSource.Current as DataRowView)["id_reader"]),

        Convert.ToDateTime(DateTime.Today));

    //обновляем данные в сетке для текущей книги

    bookUchetTableAdapter.Fill(catalogDataSet.BookUchet,

                               Convert.ToInt32((booksBindingSource.Current as DataRowView)["id_book"]));

 

    //в таблице book после выдачи книги ставим в поле label значение 1

    (booksBindingSource.Current as DataRowView)["label"] = 1;

    //сохраняем данные в первой таблице

    booksBindingSource.EndEdit();

    booksTableAdapter.Update(catalogDataSet.books);

}

 

Реализуйте функцию возврата книги. Пользователь в третьей сетке выбирает запись о выданной книге и регистрирует факт возврата. При этом в таблице uchet в поле data_in вводится текущая дата, а в поле label таблицы books ставим значение 0 (книга возвращена). Также нужно предусмотреть, что если поле data_in в таблице uchet заполнено, то вернуть книгу нельзя, так как она уже возвращена.

 

Указания: так как набор данных BookUchet создан по двум таблицам, то изменить ее можно только с помощью запроса UPDATE.

В панели "Источники данных" щелкните на кнопке "Изменить набор данных в конструкторе". В контекстном меню таблицы BooksUchet выберите команду "Добавить - Запрос", в появившемся окне выберите тип запроса UPDATE и в следующем окне введите текст запроса:

 

//для текущей записи записываем дату возврата книги

UPDATE uchet

SET data_in = ?

WHERE (id_uchet = ?)

 

На следующем шаге в качестве имени метода укажите имя UpdVydacha. В результате будет создан новый метод с параметрами UpdVydacha(data_in, id_uchet).

Нанесите на форму вторую кнопку и для кнопки введите код:

 

private void button2_Click(object sender, EventArgs e)

{

    //если поле data_in не пустое (книга уже возвращена)

    if ((bookUchetBindingSource.Current as DataRowView)["data_in"].ToString() != "")

    {

        //выдаем сообщение об ошибке

        MessageBox.Show("Указанный читатель уже вернул книгу",

                        "Операция не выполнена",

                        MessageBoxButtons.OK,

                        MessageBoxIcon.Error);

        //завершаем работу подпрограммы

        return;

    }

 

    //для записи данных о возврате книги

    //вызываем метод UpdVydacha и передаем в него параметры

    //текущую дата и id_uchet из набора данных BookVydacha

    bookUchetTableAdapter.UpdVydacha(

        Convert.ToDateTime(DateTime.Today),

        Convert.ToInt32((bookUchetBindingSource.Current as DataRowView)["id_uchet"]));

    //обновляем данные в сетке для текущей книги

    bookUchetTableAdapter.Fill(catalogDataSet.BookUchet,

                               Convert.ToInt32((booksBindingSource.Current as DataRowView)["id_book"]));

 

    //в таблице books после выдачи книги ставим в поле label значение 0

    (booksBindingSource.Current as DataRowView)["label"] = 0;

    //сохраняем данные в первой таблице

    booksBindingSource.EndEdit();

    booksTableAdapter.Update(catalogDataSet.books);

}

 

Реализуйте функцию удаления из третьей сетке записи о выданной книге. При этом, если поле data_in пустое (книга не возвращена), то удалить запись не возможно.

 

Указания: так как набор данных BookUchet создан по двум таблицам, то удалить из него данные можно только с помощью запроса DELETE.

В панели "Источники данных" щелкните на кнопке "Изменить набор данных в конструкторе". В контекстном меню таблицы BooksUchet выберите команду "Добавить - Запрос", в появившемся окне выберите тип запроса DELETE и в следующем окне введите текст запроса:

 

DELETE from uchet

where id_uchet=?

 

На следующем шаге в качестве имени метода укажите имя DelVydacha. В результате будет создан новый метод с параметрами DelVydacha(id_uchet).

Нанесите на форму третью кнопку и для кнопки введите код:

 

private void button3_Click(object sender, EventArgs e)

{

    //если в таблице нет данных

    //подпрограмма завершает работу

    if (bookUchetBindingSource.Count == 0) return;

 

    //если поле data_in пустое (книга не возвращена)

    if ((bookUchetBindingSource.Current as DataRowView)["data_in"].ToString()=="")

    {

        //выдаем сообщение об ошибке

        MessageBox.Show("Указанная книга не возвращена",

                        "Отмена удаления",

                        MessageBoxButtons.OK,

                        MessageBoxIcon.Error);

        //подпрограмма завершает работу

        return;

    }

 

    //выдаем запрос на удаление записи   

    if (MessageBox.Show("Удалить запись о выдаче книги?",

                        "Подтвердите",

                        MessageBoxButtons.YesNo,

                        MessageBoxIcon.Question,

                        MessageBoxDefaultButton.Button2)==DialogResult.Yes)

    {

        //вызываем метод DelVydacha и передаем в него параметр

        //поле id_uchet из набора данных BookVydacha

        bookUchetTableAdapter.DelVydacha(Convert.ToInt32((bookUchetBindingSource.Current as DataRowView)["id_uchet"]));

         //обновляем данные в сетке для текущей книги

        bookUchetTableAdapter.Fill(catalogDataSet.BookUchet,

                                   Convert.ToInt32((booksBindingSource.Current as DataRowView)["id_book"]));

    }

}