Техническое задание
Это аналог обычного классного журнала, слева фамилии студентов, сверху предметы на пересечении оценки - например:
только данные хранятся в базе данных :)
Варианты использования
1. Преподаватель авторизовывается, выставлеят студенту оценку по предмету.
2. Преподаватель авторизовывается, добавляет новый предмет в список предметов.
3. Преподаватель авторизовывается, добавляет нового студента в список студентов.
Функциональность
1. Проверка прав доступа: к системе имеют доступ только преподаватели. У каждого преподавателя есть свой логин и пароль.
2. Заведение студентов - каждый преподаватель может завести студета/ов.
3. Заведение предметов - каждый преподаватель может завести предмет/ы.
4. Выставление оценок - каждый преподаватель может выставить оценку по существующему предмету или изменить уже выставленную.
5. Исправление (переименование) предметов / студентов.
Доп функциональность
1. Удаление предметов / студентов / оценок
Датагрид использовать желательно, но необязательно. Можно ListView ну итд. На отображении особо не заморачивайтесь, но должно быть 'красиво' !
И самое главное:
Использовать датасет. Обеспечить корректность одновременных изменний данных.
Также должна быть обеспечена корректная работа в следующей ситуации
u1 взял данные на редактирование ( Fill )
u2 взял данные на редактирование ( Fill )
u2 сохранил данные (Update)
u1 успешно сохранил данные, имея возможность выбрать между данными u2 и cвоими, указав, какие из них попадут в базу.
Немного теории
Всегда может быть такая ситуация, когда в одном соединении мы прочитали какие-то записи, а затем попытались их обновить. Но за момент, пока мы их редактировали, а затем попытались сохранить, в другом соединении эти же записи уже были обновлены. Иначе говоря, первый процесс читает данные, после чего те же данные читает второй процесс, и второй процесс обновляет эти же данные до того, как это сможет сделать первый процесс, то возникнет конфликт, когда первый процесс попытается обновить эти данные.
Если к базе данных обращаться из нескольких соединений и проводить изменения, то возникновение конфликтов — это лишь вопрос времени и везения.
Приложение само должно решать, какие действия ему необходимо сделать, чтобы решить этот конфликт. Например, ситуация может быть такая: администратор сайта зашел на страницу, отображающую данные обычного пользователя (администратор имеет возможность обновлять эти данные).Если после того, как страница администратора прочитает пользовательские данные из базы, и обычный пользователь обратиться к странице, отображающую его пользовательские данных, и внесет измения, то возникнет конфликт, когда администратор сохранит свои изменения. Если же конфликт не возникнет — то изменения обычного пользователя будут перекрыты и потеряны. Может быть и иначе — изменения администратора потеряны. Какое поведение должно быть верным в каждом конкретном случае — это и есть сложная проблема. Первый шаг — обнаружить её. Второй — разрешить.
Реализация
В описанном ниже классе используется метод EndEdit (). Во время вызова метода EndEdit к базовому источнику данных применяются все незавершенные изменения.
Этот метод действует только в том случае, если объекты, содержащиеся в источнике данных, реализуют интерфейс IEditableObject.Если объекты не реализуют интерфейс IEditableObject, изменения в данных копируются в базовый источник данных немедленно после их внесения.Этот метод вызывает событие ListChanged.
Особенности реализации, я не буду описывать подробно, так как написала в коде комментарии.
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; namespace Journal { /// <summary> /// Основной класс, содержащий журнал успеваемости учеников /// </summary> public partial class Journal : Form { /// <summary> /// Конструктор /// </summary> public Journal() { InitializeComponent(); } /// <summary> /// Сохранение изменений /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void studentBindingNavigatorSaveItem_Click(object sender, EventArgs e) { this.Validate(); this.studentBindingSource.EndEdit(); this.studentTableAdapter.Update(this.journalDataSet.Student); } /// <summary> /// Загрузка данных в таблице /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void Journal_Load(object sender, EventArgs e) { // TODO: This line of code loads data into the 'journalDataSet.ConnectedTable' table. You can move, or remove it, as needed. this.connectedTableTableAdapter.Fill(this.journalDataSet.ConnectedTable); // TODO: This line of code loads data into the 'journalDataSet.Subject' table. You can move, or remove it, as needed. this.subjectTableAdapter.Fill(this.journalDataSet.Subject); // TODO: This line of code loads data into the 'journalDataSet.ConnectedTable' table. You can move, or remove it, as needed. this.connectedTableTableAdapter.Fill(this.journalDataSet.ConnectedTable); // TODO: This line of code loads data into the 'journalDataSet.Student' table. You can move, or remove it, as needed. this.studentTableAdapter.Fill(this.journalDataSet.Student); JournalDataSetTableAdapters.SubjectTableAdapter subjectTableAdapter = new global::Journal.JournalDataSetTableAdapters.SubjectTableAdapter(); subjectTableAdapter.Fill(this.journalDataSet.Subject); for (int j = 0; j < connectedTableDataGridView.Rows.Count - 1; j++) { int idSubject = (int)connectedTableDataGridView.Rows[j].Cells[0].Value; for (int i = 0; i < journalDataSet.Subject.Rows.Count; i++) { if ((int)journalDataSet.Subject.Rows[i]["id"] == idSubject) { connectedTableDataGridView.Rows[j].Cells["subject"].Value = journalDataSet.Subject.Rows[i]["subject"]; } } } foreach (JournalDataSet.StudentRow row in journalDataSet.Student.Rows) { cb_Name.Items.Add(row["Name"].ToString()); } foreach (JournalDataSet.SubjectRow row in journalDataSet.Subject.Rows) { cb_Subject.Items.Add(row["subject"].ToString()); } } /// <summary> /// Заполнение connectedTableDataGridView при перерисовке /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void connectedTableDataGridView_CellPainting(object sender, DataGridViewCellPaintingEventArgs e) { JournalDataSetTableAdapters.SubjectTableAdapter subjectTableAdapter = new global::Journal.JournalDataSetTableAdapters.SubjectTableAdapter(); subjectTableAdapter.Fill(this.journalDataSet.Subject); for (int j = 0; j < connectedTableDataGridView.Rows.Count - 1; j++) { int idSubject = (int)connectedTableDataGridView.Rows[j].Cells[0].Value; for (int i = 0; i < journalDataSet.Subject.Rows.Count; i++) { if ((int)journalDataSet.Subject.Rows[i]["id"] == idSubject) { connectedTableDataGridView.Rows[j].Cells["subject"].Value = journalDataSet.Subject.Rows[i]["subject"]; } } } } /// <summary> /// Добавление оценки студенту по предмету, если уже есть, то замена /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void bt_AddMark_Click(object sender, EventArgs e) { int idStudent = 0; int idSubject = 0; ; int mark = 0; try { for (int j = 0; j < journalDataSet.Student.Rows.Count - 1; j++) { if (journalDataSet.Student.Rows[j]["Name"].ToString() == cb_Name.SelectedItem.ToString()) { idStudent = (int)journalDataSet.Student.Rows[j]["id"]; break; } } for (int j = 0; j < journalDataSet.Subject.Rows.Count - 1; j++) { if (journalDataSet.Subject.Rows[j]["subject"].ToString() == cb_Subject.SelectedItem.ToString()) { idSubject = (int)journalDataSet.Subject.Rows[j]["id"]; break; } } mark = Int32.Parse(label2.Text); for (int j = 0; j < journalDataSet.ConnectedTable.Rows.Count - 1; j++) { if ((int)journalDataSet.ConnectedTable.Rows[j]["id_subject"] == idSubject) { if ((int)journalDataSet.ConnectedTable.Rows[j]["id_student"] == idStudent) { label1.Text = cb_Name.SelectedItem.ToString() + " исправил оценку " + journalDataSet.ConnectedTable.Rows[j]["mark"].ToString() + " по " + cb_Subject.SelectedItem.ToString() + " на " + mark.ToString(); journalDataSet.ConnectedTable.Rows[j]["mark"] = mark; label1.Refresh(); return; } } } JournalDataSet.ConnectedTableRow newRow; newRow = journalDataSet.ConnectedTable.NewConnectedTableRow(); newRow.id_student = idStudent; newRow.id_subject = idSubject; newRow.mark = mark; journalDataSet.ConnectedTable.AddConnectedTableRow(newRow); label1.Text = cb_Name.SelectedItem.ToString() + " получил " + mark.ToString() + " по " + cb_Subject.SelectedItem.ToString(); //journalDataSet.ConnectedTable.AcceptChanges(); } finally { this.connectedTableTableAdapter.Update(this.journalDataSet.ConnectedTable); } } /// <summary> /// Обработка события смены значений radiobutton /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void radioButton3_CheckedChanged(object sender, EventArgs e) { RadioButton rb = (RadioButton)sender; label2.Text = rb.Text; } /// <summary> /// Кнопка выполняет Refresh /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void toolStripButton1_Click(object sender, EventArgs e) { Journal_Load(this, new EventArgs()); } private void toolStripButton16_Click(object sender, EventArgs e) { this.Validate(); this.studentBindingSource.EndEdit(); this.studentTableAdapter.Update(this.journalDataSet.Student); } private void toolStripButton24_Click(object sender, EventArgs e) { this.Validate(); this.subjectBindingSource.EndEdit(); this.subjectTableAdapter.Update(this.journalDataSet.Subject); } } } |
Опишем форму ввода логина и пароля.
using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Text; using System.Windows.Forms; using System.Data.SqlClient; using System.Data.OleDb; using System.Configuration; namespace Journal { public partial class Connection : Form { /// <summary> /// Класс проверки пользователя и пороля и подключения к базе /// </summary> public Connection() { InitializeComponent(); } /// <summary> /// Пробуем подключиться к SQLServer /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void bt_Connection_Click(object sender, EventArgs e) { // Create a SQL Connection and DataAdapter SqlConnection conn = new SqlConnection(ConfigurationSettings.AppSettings["Sql_ConnectString"]); SqlCommand cm = new SqlCommand(); // call provider independent function to retrieve data GetData(conn, cm); } /// <summary> /// Пробуем подключиться к БД /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void GetData(IDbConnection conn, IDbCommand cm) { try { conn.Open(); // create the command and assign it to the IDbDataAdapter interface IDbCommand cmd = conn.CreateCommand(); int count = 0; cmd.CommandText = "SELECT Count(*) FROM ConnectLogin WHERE Login = '" + tb_Login.Text + "' AND Password = '" + tb_Password.Text + "'"; count = (int)cmd.ExecuteScalar(); if (count > 0) { this.Close(); Program.isConnected = true; } else MessageBox.Show("Не удалось подключиться к базе данных, не верная пара логин-пороль"); } catch { MessageBox.Show("Не верно указано имя сервера"); } finally { conn.Close(); } } } } |
Также приведу схему базы данных, используемую в приложении.
Рис 1. Схема БД приложения 'Журнал успеваемости'
А можно файл программы?
ОтветитьУдалить