【实战指南】C#与ADO.NET:构建图书管理系统从界面到数据库
1. 环境准备与项目创建
在开始构建图书管理系统之前,我们需要准备好开发环境。首先确保已安装Visual Studio 2022(社区版即可)和SQL Server Express。打开Visual Studio后,选择"新建项目",在模板中选择"Windows窗体应用(.NET Framework)",命名为"BookManagementSystem"。
我习惯在解决方案资源管理器中先创建三个文件夹:
- Models :存放数据模型类
- DAL :数据访问层
- UI :用户界面相关文件
接下来需要安装必要的NuGet包。右键项目选择"管理NuGet程序包",搜索并安装:
- Microsoft.Data.SqlClient(最新的ADO.NET提供程序)
- System.Data.DataSetExtensions(用于数据绑定)
提示:如果使用SQL Server身份验证而非Windows身份验证,连接字符串需要包含用户名和密码。生产环境中建议将连接字符串加密存储在配置文件中。
2. 数据库设计与建表
图书管理系统的核心是数据库设计。打开SQL Server Management Studio,新建名为"BookDB"的数据库。我们需要创建三张基础表:
-- 管理员表
CREATE TABLE t_admin (
id VARCHAR(20) PRIMARY KEY,
psw VARCHAR(50) NOT NULL
);
-- 图书表
CREATE TABLE t_book (
isbn VARCHAR(20) PRIMARY KEY,
name NVARCHAR(100) NOT NULL,
author NVARCHAR(50),
publisher NVARCHAR(100),
stock INT DEFAULT 0
);
-- 借阅记录表
CREATE TABLE t_lend (
id INT IDENTITY(1,1) PRIMARY KEY,
user_id VARCHAR(20) NOT NULL,
book_isbn VARCHAR(20) NOT NULL,
lend_date DATETIME DEFAULT GETDATE(),
return_date DATETIME,
FOREIGN KEY (book_isbn) REFERENCES t_book(isbn)
);
实测中发现几个常见问题需要注意:
- ISBN字段长度应根据实际需求设置,国际标准是13位
- 图书名称和作者字段建议使用NVARCHAR以支持中文
- 借阅记录的归还日期应为NULL表示未归还
3. ADO.NET核心数据访问层
在DAL文件夹中创建Dao.cs,这是整个系统的数据库操作核心。我采用单例模式来管理数据库连接:
using Microsoft.Data.SqlClient;
using System.Configuration;
namespace BookMS.DAL
{
public class Dao
{
private static SqlConnection _connection;
private static readonly string _connStr = ConfigurationManager.ConnectionStrings["BookDB"].ConnectionString;
private Dao() { }
public static SqlConnection GetConnection()
{
if (_connection == null)
{
_connection = new SqlConnection(_connStr);
_connection.Open();
}
else if (_connection.State == System.Data.ConnectionState.Closed)
{
_connection.Open();
}
return _connection;
}
public static int ExecuteNonQuery(string sql, params SqlParameter[] parameters)
{
using (var cmd = new SqlCommand(sql, GetConnection()))
{
cmd.Parameters.AddRange(parameters);
return cmd.ExecuteNonQuery();
}
}
public static SqlDataReader ExecuteReader(string sql, params SqlParameter[] parameters)
{
var cmd = new SqlCommand(sql, GetConnection());
cmd.Parameters.AddRange(parameters);
return cmd.ExecuteReader();
}
}
}
踩过的坑:早期版本没有妥善处理连接关闭问题,导致连接池耗尽。后来改用using语句确保资源释放,关键业务操作使用事务:
public static bool ExecuteTransaction(params SqlCommand[] commands)
{
using (var transaction = GetConnection().BeginTransaction())
{
try
{
foreach (var cmd in commands)
{
cmd.Connection = GetConnection();
cmd.Transaction = transaction;
cmd.ExecuteNonQuery();
}
transaction.Commit();
return true;
}
catch
{
transaction.Rollback();
throw;
}
}
}
4. 登录模块实现
登录界面需要区分管理员和普通用户。在UI文件夹中添加Login窗体,包含:
- 两个TextBox(用户名和密码)
- 两个RadioButton(用户类型选择)
- 一个Button(登录)
核心登录逻辑:
private void btnLogin_Click(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(txtUser.Text) || string.IsNullOrEmpty(txtPwd.Text))
{
MessageBox.Show("用户名或密码不能为空");
return;
}
string tableName = radioAdmin.Checked ? "t_admin" : "t_user";
string sql = $"SELECT * FROM {tableName} WHERE id=@id AND psw=@psw";
var parameters = new[]
{
new SqlParameter("@id", txtUser.Text),
new SqlParameter("@psw", txtPwd.Text) // 实际项目应该加密
};
using (var reader = Dao.ExecuteReader(sql, parameters))
{
if (reader.Read())
{
Data.UID = reader["id"].ToString();
Data.UName = reader["name"]?.ToString();
var mainForm = radioAdmin.Checked
? (Form)new AdminMainForm()
: new UserMainForm();
this.Hide();
mainForm.ShowDialog();
this.Show();
}
else
{
MessageBox.Show("登录失败,请检查凭证");
}
}
}
安全提示:实际项目中密码应该使用哈希加盐存储,SQL语句要参数化防止注入。我曾在早期版本直接拼接SQL字符串,结果被测试人员轻松注入,这个教训很深刻。
5. 图书管理模块开发
管理员界面主要包含DataGridView显示图书列表,以及增删改查功能。先创建AdminMainForm,添加MenuStrip控件设置菜单项。
图书管理窗体(AdminBookForm)的关键代码:
public partial class AdminBookForm : Form
{
private BindingSource _bindingSource = new BindingSource();
public AdminBookForm()
{
InitializeComponent();
dgvBooks.AutoGenerateColumns = false;
LoadBooks();
}
private void LoadBooks()
{
string sql = "SELECT isbn, name, author, publisher, stock FROM t_book";
var adapter = new SqlDataAdapter(sql, Dao.GetConnection());
var dataSet = new DataSet();
adapter.Fill(dataSet, "Books");
_bindingSource.DataSource = dataSet.Tables["Books"];
dgvBooks.DataSource = _bindingSource;
}
private void btnAdd_Click(object sender, EventArgs e)
{
var form = new BookEditForm();
if (form.ShowDialog() == DialogResult.OK)
{
string sql = @"INSERT INTO t_book
VALUES (@isbn, @name, @author, @publisher, @stock)";
var parameters = new[]
{
new SqlParameter("@isbn", form.ISBN),
// 其他参数...
};
if (Dao.ExecuteNonQuery(sql, parameters) > 0)
{
MessageBox.Show("添加成功");
LoadBooks();
}
}
}
}
数据绑定技巧:设置DataGridView的AutoGenerateColumns为false,手动配置列与数据字段的映射关系。这样能精确控制显示格式:
private void ConfigureDataGridView()
{
dgvBooks.Columns.Add(new DataGridViewTextBoxColumn()
{
DataPropertyName = "isbn",
HeaderText = "ISBN",
Width = 150
});
// 其他列配置...
}
6. 借阅功能实现
用户界面需要实现图书查询和借阅功能。在UserMainForm中添加搜索面板和图书列表:
private void btnSearch_Click(object sender, EventArgs e)
{
string keyword = "%" + txtKeyword.Text + "%";
string sql = @"SELECT * FROM t_book
WHERE name LIKE @keyword OR author LIKE @keyword";
var adapter = new SqlDataAdapter(sql, Dao.GetConnection());
adapter.SelectCommand.Parameters.AddWithValue("@keyword", keyword);
var table = new DataTable();
adapter.Fill(table);
dgvBooks.DataSource = table;
}
private void btnBorrow_Click(object sender, EventArgs e)
{
if (dgvBooks.CurrentRow == null) return;
string isbn = dgvBooks.CurrentRow.Cells["isbn"].Value.ToString();
string sql = "INSERT INTO t_lend (user_id, book_isbn) VALUES (@uid, @isbn)";
var parameters = new[]
{
new SqlParameter("@uid", Data.UID),
new SqlParameter("@isbn", isbn)
};
try
{
if (Dao.ExecuteNonQuery(sql, parameters) > 0)
{
MessageBox.Show("借阅成功");
}
}
catch (SqlException ex)
{
if (ex.Number == 547) // 外键约束错误
MessageBox.Show("图书不存在");
else
throw;
}
}
性能优化点:当图书数据量大时,应该实现分页查询而不是一次性加载所有数据。可以使用ROW_NUMBER()实现分页:
WITH BookPage AS (
SELECT ROW_NUMBER() OVER (ORDER BY name) AS RowNum, *
FROM t_book
WHERE name LIKE @keyword
)
SELECT * FROM BookPage
WHERE RowNum BETWEEN @start AND @end
7. 报表与统计功能
管理员通常需要查看借阅统计。我们可以使用Chart控件可视化数据:
private void LoadStats()
{
string sql = @"SELECT b.name, COUNT(l.id) AS lend_count
FROM t_book b LEFT JOIN t_lend l ON b.isbn = l.book_isbn
GROUP BY b.name
ORDER BY lend_count DESC";
var adapter = new SqlDataAdapter(sql, Dao.GetConnection());
var dataSet = new DataSet();
adapter.Fill(dataSet);
chart1.Series["Series1"].Points.DataBind(
dataSet.Tables[0].DefaultView, "name", "lend_count", "");
}
对于复杂报表,我推荐使用ReportViewer控件或第三方报表工具如DevExpress。曾经一个项目需要导出Excel报表,直接用DataTable导出性能很差,后来改用ClosedXML库效率提升明显。
8. 系统部署与维护
完成开发后,需要准备安装包。在Visual Studio中添加安装项目,包含:
- 主程序文件
- .NET Framework运行环境(如果目标机器没有)
- 数据库脚本
- 配置文件模板
数据库部署建议:
- 准备SQL脚本创建数据库和表
- 包含初始数据插入脚本(如管理员账户)
- 考虑添加数据库升级脚本
维护技巧:在app.config中配置连接字符串时,可以使用配置文件转换功能为不同环境(开发/测试/生产)设置不同的连接字符串:
<connectionStrings>
<add name="BookDB" connectionString="..." providerName="System.Data.SqlClient" />
</connectionStrings>
日志记录:建议使用log4net记录系统操作日志和异常,便于故障排查。我在Dao类中添加了日志记录后,排查数据库问题效率提高了许多。
更多推荐


所有评论(0)