实验基础信息
开发环境:
| 操作系统 | Microsoft Windows 7 |
| -------------- | --------------------------- |
| 数据库管理系统 | Microsoft SQL Server 2012 |
| 编程工具 | Microsoft VisualStudio 2013 |
| 编程语言 | C# |
界面演示
部分全局变量:
List<string> SQLToBeDone; //存储需要执行的SQL语句Dictionary<int,string[]> values; //存储待INSERT的值DataSet orderDetails; //数据集,存储适配器选择出来的数据OleDbDataAdapter ordAdapter; //OleDb数据适配器
查询功能:
(见下页)

可见,程序开启时默认显示所有订单及第一个订单的详情
private void Form1_Load(object sender, EventArgs e){//显示所有订单this.salesOrderHeaderTableAdapter.Fill(this.adventureDataSet.SalesOrderHeader);orderDetails = new DataSet();//显示第一个订单详情int SaleOrderID = Int32.Parse(this.salesGridView.Rows[0].Cells[0].Value.ToString());String salesDetailQuery = "SELECT * FROM Sales.SalesOrderDetail WHERE SalesOrderID = " + SaleOrderID.ToString();ordAdapter = new OleDbDataAdapter(salesDetailQuery, this.salesOrderDetailTableAdapter.Connection);ordAdapter.Fill(orderDetails);this.salesDetailGridView.DataSource = orderDetails.Tables[0];}
单击某一行,显示对应行的订单详情
private void salesGridView_CellClick(object sender, DataGridViewCellEventArgs e){if (salesGridView.SelectedRows.Count > 0){int SaleOrderID = Int32.Parse(this.salesGridView.SelectedRows[0].Cells[0].Value.ToString());String salesDetailQuery = "SELECT * FROM Sales.SalesOrderDetail WHERE SalesOrderID = " + SaleOrderID.ToString();ordAdapter = new OleDbDataAdapter(salesDetailQuery, this.salesOrderDetailTableAdapter.Connection);orderDetails.Clear();ordAdapter.Fill(orderDetails);this.salesDetailGridView.DataSource = orderDetails.Tables[0];}}
删除功能:
选取订单明细中的一行,点击删除,选择的订单明细从表中删除
(见下页)


删除按钮代码如下
(见下页)
private void delete_btn_Click(object sender, EventArgs e){if (salesDetailGridView.SelectedRows.Count > 0){int SaleOrderDetailID = Int32.Parse(this.salesDetailGridView.SelectedRows[0].Cells[1].Value.ToString());String salesDetailDelete = "DELETE FROM Sales.SalesOrderDetail WHERE SalesOrderDetailID = " + SaleOrderDetailID.ToString();SQLToBeDone.Add(salesDetailDelete); orderDetails.Tables[0].Rows[this.salesDetailGridView.SelectedRows[0].Index].Delete();}else{MessageBox.Show("请先选中要删除的行");}}
但并不真正从数据库中删除(因为生成的SQL语句存储在
SQLToBeDone
中,此时还未执行)
关闭功能:

续上述操作,点击关闭按钮或者右上角的X按钮时:
点击关闭按钮时,如果订单明细有修改但没有保存,则提示是否关闭,如果不关闭则返回,否则关闭程序,如果没有修改,则直接关闭(退出前检查SQLToBeDone 是否有内容,如果有则说明做出了修改)
protected override void OnFormClosing(FormClosingEventArgs e){if (SQLToBeDone.Count > 0 && CloseCancel() == false){e.Cancel = true;};}public static bool CloseCancel(){const string message = "你还有未保存的修改,确认要退出吗?";const string caption = "退出确认窗口";var result = MessageBox.Show(message, caption,MessageBoxButtons.YesNo,MessageBoxIcon.Question);if (result == DialogResult.Yes)return true;elsereturn false;}private void close_btn_Click(object sender, EventArgs e){this.Close();}
保存功能:
续上述操作,选择
否
后, 单击保存按钮点击保存按钮时,将订单明细保存到数据库
(见下页)
private void save_btn_Click(object sender, EventArgs e){bool hasError = false;if (SQLToBeDone.Count > 0){this.salesOrderDetailTableAdapter.Connection.Open();foreach(String sql in SQLToBeDone){try{OleDbCommand cmd = new OleDbCommand(sql, this.salesOrderDetailTableAdapter.Connection);//创建Command对象cmd.ExecuteNonQuery();//执行命令}catch (Exception ex){hasError = true;MessageBox.Show(ex.Message, "数据库操作错误", MessageBoxButtons.OK, MessageBoxIcon.Error);}}this.salesOrderDetailTableAdapter.Connection.Close();SQLToBeDone.Clear();if (!hasError){MessageBox.Show("保存成功");}}}
保存按钮按下之后: 

再次查询数据库:

发现对应记录已经被删除
插入功能:
可以在订单明细表的最后输入新的订单明细(在输入完一行之后,插入操作才被加入队列中)

保存后,查询数据库有:
(见下页)

更新功能:可以修改订单明细表中的某一行的内容

保存后查询数据库有:

如果输入的单价(UnitPrice)大于产品的公开报价,则提示相应的信息(利用4.6.2的触发器完成此功能)。

对应保存与更新功能的代码部分
通过
Dictionary
及 List
数据结构,判断当前行是否已输入所有需要输入的数据根据是否有
SalesOrderDetailID
来判断是进行插入操作还是更新操作private void salesGridDetailView_CellValidated(object sender, DataGridViewCellEventArgs e){string cellValue = salesDetailGridView.Rows[e.RowIndex].Cells[e.ColumnIndex].Value.ToString();string id = salesDetailGridView.Rows[e.RowIndex].Cells[1].Value.ToString();string columnName = salesDetailGridView.Columns[e.ColumnIndex].Name;if (id.Length > 0){//UPDATE 模式String salesDetailUpdate = "UPDATE Sales.SalesOrderDetail SET " + columnName + " = '" + cellValue + "' WHERE SalesOrderDetailID = " + id;SQLToBeDone.Add(salesDetailUpdate);}else{//INSERT 模式if (!values.ContainsKey(e.RowIndex)){string[] lineValue = new string[11];lineValue[e.ColumnIndex] = cellValue;values[e.RowIndex] = lineValue;}else{values[e.RowIndex][e.ColumnIndex] = cellValue;int length = 0;for (int i = 0; i < 11; ++i){length += (values[e.RowIndex][i] == string.Empty || values[e.RowIndex][i] == null ? 0 : 1);}string salesDetailInsert = "INSERT INTO Sales.SalesOrderDetail VALUES(";if (length == 9){for (int i = 0; i < 11; ++i){if (i != 1 && i != 8){salesDetailInsert += "'";salesDetailInsert += values[e.RowIndex][i];salesDetailInsert += "'";if (i != 10){salesDetailInsert += ",";}}}salesDetailInsert += ")";SQLToBeDone.Add(salesDetailInsert);values.Remove(e.RowIndex);}}}}
问题解答
-
使用哪种数据提供程序?本程序使用了 OLEDB 类数据提供程序
-
使用的数据连接对象是哪一个?连接对象是如何建立的?最后生成的连接对象中的连接字符串是什么?代表什么含义?连接对象为this.salesOrderDetailTableAdapter.Connection是使用salesOrderDetailTableAdapter 使用SQL语句 连接对应的数据库 建立的连接字符串如下:Provider=SQLNCLI11;Data Source=Nathaniel-PC;Persist Security Info=True;Password=123123;User ID=test_login;Initial Catalog=AdventureWorks2012含义如下Provider 为客户端组件的驱动 Data Source 为数据源的名称Password 为连接数据库的密码 User ID 为连接数据库的登录名Initial Catalog为默认使用的数据库 Persist Security Info 表示是否保存用户登录信息
-
使用的数据适配器对象是什么?其中的查询或更新语句是什么?如果有参数则参数是如何处理的?使用的数据适配器对象为ordAdapter查询语句为"SELECT * FROM Sales.SalesOrderDetail WHERE SalesOrderID = " + SaleOrderID.ToString();更新语句为"UPDATE Sales.SalesOrderDetail SET " + columnName + " = '" + cellValue + "' WHERE SalesOrderDetailID = " + id;参数使用 ToString()方法 连接入SQL语句中
-
使用的数据集对象是什么?数据集中有哪些数据表?数据表是由哪些适配器对象生成的?(或采用其它方法)使用的数据集对象为orderDetails 内部有SalesOrderDetail表,是由ordAdapter数据适配器对象生成的还有一个数据集为AdventureDataSet 内部有SalesOrderHeader表,是由salesOrderHeaderTableAdapter数据适配器对象生成的
总结
常见问题
- 在实验过程中总是在出现数据库操作异常后程序中止,后来将数据库操作放入
try... catch
语句块中后,数据库错误得以被显示出来,而且程序得以继续正常运行 - 在一开始设计器中自动生成的
cell_content_click
方法下,实现点击订单列表栏目自动显示对应详情总是失败,后来更换为cell_click
便成功解决了这一问题
需要改进的地方
- 程序可移植性差,部分列采用列序号硬编码方式,更换数据库后该程序可能不能再适用
- 程序界面适应性差,在窗口大小变化后,内容并不会随窗口大小变化而自适应