一、功能整体介绍
本窗体实现同一个窗口复用 新增数据 / 编辑数据双功能:
-
通过重载构造函数传递标记
isEdit、主键ID 区分新增/编辑模式 -
下拉框动态绑定性别数据(全部/男/女),设置显示值与实际值
-
编辑模式:根据ID参数化查询数据库,回显数据到界面控件
-
新增/编辑提交:参数化执行 Insert / Update 语句,杜绝SQL注入
-
前端数据合法性校验(非空、年龄范围、禁止选择默认全部项)
-
分级异常捕获(数据库异常、程序通用异常)
-
使用 using 自动释放数据库连接、命令资源
-
连接字符串写入 App.config 配置文件,便于统一维护
二、前置准备(必须配置)
1. 引入命名空间
cs
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using System.Windows.Forms;
2. App.config 配置数据库连接字符串
cs
<configuration>
<connectionStrings>
<add name="connectionString"
connectionString="server=192.168.215.96,1344;database=StudentDataBase;uid=sa;pwd=123456"
providerName="System.Data.SqlClient"/>
</connectionStrings>
</configuration>
3. 性别下拉框实体类(辅助类)
用于绑定下拉框 文本+值 对应关系
cs
public class Sex
{
// 下拉显示文本
public string Name { get; set; }
// 下拉对应真实值
public int Value { get; set; }
}
三、核心窗体完整代码(去重+规范优化)
cs
namespace _01创建数据库
{
public partial class Form2 : Form
{
// 标记:是否为编辑模式
private bool isEdit;
// 编辑数据的主键ID
private int editId;
// 默认构造:新增模式
public Form2()
{
InitializeComponent();
isEdit = false;
editId = 0;
}
// 重载构造:支持外部传入编辑标记和ID
public Form2(bool isedit = false, int editId = 0) : this()
{
this.isEdit = isedit;
this.editId = editId;
}
// 读取配置文件数据库连接字符串
public string connectionString = ConfigurationManager.ConnectionStrings["connectionString"].ConnectionString;
private void Form2_Load(object sender, EventArgs e)
{
// 1.初始化性别下拉框
cobxSex.DataSource = new List<Sex>()
{
new Sex(){Name="全部",Value=-1},
new Sex(){Name="男",Value=0},
new Sex(){Name="女",Value=1},
};
cobxSex.DisplayMember = "Name"; // 界面显示字段
cobxSex.ValueMember = "Value"; // 后台取值字段
// 2.判断模式:编辑 / 新增
if (isEdit)
{
// 编辑模式:根据ID查询数据回显
this.Text = "编辑";
btnAdd.Text = "编辑";
using (SqlConnection coon = new SqlConnection(connectionString))
{
if (coon.State != ConnectionState.Open)
coon.Open();
// 参数化查询(防SQL注入)
string sql = "select * from First where Id = @Id";
SqlCommand cmd = new SqlCommand(sql, coon);
cmd.Parameters.Add(new SqlParameter("@Id", SqlDbType.Int) { Value = editId });
SqlDataReader reader = cmd.ExecuteReader();
if (reader.Read())
{
// 回显姓名
txtName.Text = reader["Name"].ToString();
// 回显年龄
numAge.Value = (int)reader["Age"];
// 性别空值判断
if (reader.IsDBNull(reader.GetOrdinal("Sex")))
cobxSex.SelectedValue = -1;
else
cobxSex.SelectedValue = (bool)reader["Sex"] ? 1 : 0;
// 生日空值判断
if (!reader.IsDBNull(reader.GetOrdinal("Birthday")))
dateBirthday.Value = Convert.ToDateTime(reader["Birthday"]);
else
dateBirthday.Value = DateTime.Now;
}
reader.Close();
}
}
else
{
// 新增模式
this.Text = "添加";
btnAdd.Text = "添加";
}
}
// 提交按钮:新增 / 编辑 统一提交
private void btnAdd_Click(object sender, EventArgs e)
{
#region 前端数据校验(防呆)
string name = txtName.Text.Trim();
if (string.IsNullOrEmpty(name))
{
MessageBox.Show("姓名不能为空");
txtName.Focus();
return;
}
int age = (int)numAge.Value;
if (age < 0 || age > 120)
{
MessageBox.Show("年龄不合法");
numAge.Focus();
return;
}
int sexNum = (int)cobxSex.SelectedValue;
if (sexNum == -1)
{
MessageBox.Show("请选择男或女,不能选择全部");
cobxSex.Focus();
return;
}
// 转换性别布尔值
bool sex = sexNum == 1;
DateTime birthday = dateBirthday.Value;
#endregion
string sql = string.Empty;
List<SqlParameter> paramList = new List<SqlParameter>();
#region 拼接SQL与参数
if (isEdit)
{
// 编辑语句:Update
sql = @"Update First set Name = @Name ,Age= @Age ,Sex=@Sex , Birthday = @Birthday where Id =@Id";
paramList.Add(new SqlParameter("@Id", editId));
}
else
{
// 新增语句:Insert
sql = @"insert into First(Name,Age,Sex,Birthday) values(@Name ,@Age ,@Sex ,@Birthday)";
}
// 公共参数
paramList.Add(new SqlParameter("@Name", name));
paramList.Add(new SqlParameter("@Age", age));
paramList.Add(new SqlParameter("@Sex", sex));
paramList.Add(new SqlParameter("@Birthday", birthday));
#endregion
#region 数据库执行 + 异常捕获
try
{
using (SqlConnection conn = new SqlConnection(connectionString))
{
using (SqlCommand cmd = new SqlCommand(sql, conn))
{
cmd.Parameters.AddRange(paramList.ToArray());
conn.Open();
// 增删改执行方法,返回受影响行数
int rows = cmd.ExecuteNonQuery();
if (rows > 0)
{
MessageBox.Show(isEdit ? "编辑成功" : "新增成功");
this.DialogResult = DialogResult.OK;
this.Close();
}
else
{
MessageBox.Show("操作失败,未修改任何数据");
}
}
}
}
catch (SqlException ex)
{
MessageBox.Show($"数据库异常:{ex.Message}", "操作失败", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
catch (Exception ex)
{
MessageBox.Show($"程序异常:{ex.Message}", "操作失败", MessageBoxButtons.OK, MessageBoxIcon.Error);
}
#endregion
}
}
}
四、核心考点详解(期末必考)
1. 窗口复用原理
-
通过重载构造函数,外部可传参标记新增/编辑状态
-
isEdit=false:新增模式,清空界面、执行Insert -
isEdit=true:编辑模式,根据editId回显数据、执行Update
2. 配置文件优势
-
连接字符串统一放在App.config,无需改代码、直接改配置即可切换数据库
-
避免硬编码,项目规范、便于维护
3. 参数化查询核心作用
-
彻底防止SQL注入攻击
-
自动处理特殊字符、数据类型转换,避免语法报错
4. using资源释放
SqlConnection、SqlCommand 实现了IDisposable接口,using执行完毕自动关闭连接、释放资源,杜绝数据库连接泄露。
5. 空值处理
使用 reader.IsDBNull() 判断数据库可空字段,避免读取空数据时报错,是数据库项目必备容错写法。
6. ExecuteNonQuery 方法
专门用于增、删、改 操作,返回值为受影响的数据行数,大于0代表操作成功。
五、调用方式(父窗体打开弹窗)
cs
// 打开新增窗口
Form2 addForm = new Form2();
addForm.ShowDialog();
// 打开编辑窗口(假设编辑ID=1)
Form2 editForm = new Form2(true,1);
editForm.ShowDialog();
六、代码优化亮点(考试加分点)
-
去除重复代码,统一新增/编辑提交逻辑
-
分级异常捕获,区分数据库错误和程序错误
-
完善前端校验,拦截非法数据
-
下拉框绑定实体类,规范性强
-
所有数据库操作使用using,符合.NET最优规范
七、绑定数据源过程
-
创建链接,并且打开 SqlConnection conn = new SqlConnection(connectionString);
-
创建一个传递参数的对象 如果传递多个参数 定义成参数数组 SqlParameter\[\] sqlParas = new SqlParameter\[\]
-
把参数赋值 sqlParas0.Value 或 List<SqlParameter> list = new List<SqlParameter>();
-
组织Sql语句
-
创建一个命令 SqlCommand cmd = new SqlCommand (sql, conn);
-
开始执行sql遇见执行之前把参数数组清空 cmd.Parameters.Clear (); cmd.Parameters.AddRange (sqlParas);//添加Sql语句参数 SqlDataReader reader = cmd.ExecuteReader ();
-
取出数据 展示到界面上 DataTable dt=new DataTable(); dt.Load(reader);
dataGridView1.DataSource = dt;
-
关闭连接