前言
- 本文我们将通过Windows C#的桌面应用程序来做一个学生数据库查询系统,其中学生数据库的信息是存储在Access数据库的
.accdb
文件中 - 本教程包含如下内容(注:本教程只设计一些基础内容进行教学,不考虑安全性和美观以及用户体验):
- 主界面
- 登录界面
- 注册界面
- 数据库增删改查界面
- 下面是本教程的使用到的环境,本教程不涉及如何安装环境和配置,请大家自行移步去其他教程搜寻~
Office 2021 Mircosoft Acess
(可以考虑使用office tool plus
整套替换)Visual Studio 2022
- 往期回顾:
- 上一期我们谈论了如何使用Windows C# 桌面窗体应用程序去设计我们整个学生数据库管理系统的程序UI和代码框架,这一期我们来聊聊如何对Acess数据库进行连接和SQL增删改查的逻辑
1 创建Access数据库
-
我们打开Access
-
在我们上一节的文件夹下创建一个数据库
-
让我们为学生登录界面的账户信息新建一个表取名为
StudentsLogin
,然后简单添加如下内容,这里我们为了测试做了一下几点- 字段设置
学号
设置为数字
姓名
和性别
设置为短文本
,出生日期
设置为时间
- 设置相同名字但不不同性别的,设置相同名字相同性别但是不不同级的同学
- 字段设置
2 数据库连接
2-1 配置VS数据库连接
-
我们来连接
Access
数据库,在左上角工具栏工具找到连接到数据库 -
选择
.accdb
数据库文件 -
点击左下角
测试连接
,当出现连接成功说明成功和数据库建立连接 -
然后我们点开高级,在
Provider
里找到复制一下路径
Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Users\lzh\Desktop\StudentDatasetsExplorer\StudentDatabases.accdb
- 点击确认后我们就可以看到数据库连接成功
2-2 数据库连接代码
- 我们在项目中创建一个
StudentDataBasesConnector.cs
,专门负责建立数据库连接的类 - 注意这里的
connectionString
需要替换为上面连接的字符串
using System;
using System.Collections.Generic;
using System.Data.OleDb;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace StudentDatasetsExplorer
{
internal class StudentsDataBaseConnector
{
public void Connect2DataBase()
{
string connectionString = @"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Users\lzh\Desktop\StudentDatasetsExplorer\StudentDatabases.accdb";
using (OleDbConnection connection = new OleDbConnection(connectionString))
{
try
{
connection.Open();
MessageBox.Show("data base connected!");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
}
}
}
- 同时我们在Program.cs中添加如下内容,进行数据库连接测试
static void Main()
{
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
StudentsDataBaseConnector studentsDataBaseConnector = new StudentsDataBaseConnector();
studentsDataBaseConnector.Connect2DataBase();
Application.Run(new MainForm());
}
2-3 环境修正
-
运行上述代码以后,我们会发现数据库连接异常,我们来解决这个问题
-
Microsoft.ACE.OLEDB.12.0
是一个用于访问Microsoft Access数据库的提供程序,它通常随Microsoft Access数据库引擎一起安装。这里我们补充下载一下Microsoft Access Database Engine
-
这里我们选择
accessdatabaaseengine.exe
下载 -
下载后我们会出现以下的程序
-
我们打开cmd,进入你的下载路径,输入下述指令
AccessDatabaseEngine.exe /quiet
- 等待操作后我们重新回到程序,运行
3 数据库连接类—单例模式
- 考虑我们整个程序到需要用到同一份数据库,如果我们多次进行实例化进行多次数据的拷贝,这是一个资源容易出现溢出浪费的现象,熟悉设计模式的朋友们这时候可以就知道了,这里我们引入
单例模式
- 单例模式(Singleton Pattern)是一种设计模式,它确保一个类只有一个实例,并提供一个全局访问点来获取这个实例。单例模式通常用于管理共享资源,如数据库连接、文件系统、应用程序配置等,这些资源在应用程序的生命周期内只需要一个实例。
- 单例模式的主要特点包括:
- 唯一实例:单例类确保只能创建一个实例。
- 全局访问点:单例类提供了一种全局访问点,通过这个访问点可以获取到该类的唯一实例。
- 延迟创建:单例实例通常在第一次被请求时创建,而不是在类加载时创建。这种技术称为延迟初始化。
- 单例模式的实现通常涉及以下几个步骤:
- 私有构造函数:确保不能通过
new
关键字直接创建类的实例。 - 私有静态变量:用于存储类的唯一实例。
- 私有构造函数:确保不能通过
-----------------------------------------
| Singleton |
-----------------------------------------
| - instance: Singleton |
-----------------------------------------
| + Singleton() [private] |
| + getInstance(): Singleton |
-----------------------------------------
- 我们修改
StudentDataBasesConnector.cs
using System;
using System.Collections.Generic;
using System.Data.OleDb;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace StudentDatasetsExplorer
{
internal class StudentsDataBaseConnector
{
private OleDbConnection connection;
private static StudentsDataBaseConnector instance;
private StudentsDataBaseConnector()
{
string connectionString = @"Provider=Microsoft.ACE.OLEDB.12.0;Data Source=C:\Users\lzh\Desktop\StudentDatasetsExplorer\StudentDatabases.accdb";
connection = new OleDbConnection(connectionString);
}
public static StudentsDataBaseConnector GetInstance()
{
if (instance == null)
{
instance = new StudentsDataBaseConnector();
}
return instance;
}
public OleDbConnection GetStudentDataBaseConnection()
{
try
{
connection.Open();
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
return connection;
}
protected object MemberwiseClone()
{
return this;
}
}
}
- 如果我们要使用
StudentsDataBaseConnector
类:
StudentsDataBaseConnector studentsDataBaseConnector = StudentsDataBaseConnector.GetInstance();
4 SQL
4-1 介绍
- SQL(结构化查询语言)是一种用于管理和操作关系数据库管理系统(RDBMS)的标准语言。它允许用户执行各种操作,如查询、更新、插入和删除数据库中的数据。SQL也可以用于创建和管理数据库对象,如表、视图和索引。
- SQL的基础语法包括以下主要部分:
- SELECT:用于从数据库中选择数据。基本语法是
SELECT column1, column2, ... FROM table_name
。 - WHERE:用于过滤记录。它后面通常跟着一个条件,只有满足条件的记录才会被选中。例如,
WHERE condition
。 - INSERT INTO:用于向表中插入新记录。基本语法是
INSERT INTO table_name (column1, column2, ...) VALUES (value1, value2, ...)
。 - UPDATE:用于更新表中的现有记录。基本语法是
UPDATE table_name SET column1 = value1, column2 = value2, ... WHERE condition
。 - DELETE:用于删除表中的记录。基本语法是
DELETE FROM table_name WHERE condition
。 - CREATE TABLE:用于创建新表。基本语法是
CREATE TABLE table_name (column1 datatype, column2 datatype, ...)
。 - ALTER TABLE:用于修改现有表的结构。例如,添加新列、删除列或修改列的数据类型。
- DROP TABLE:用于删除整个表,包括所有其包含的记录。
- JOIN:用于结合两个或多个表的行,基于这些表之间的相关列。
- UNION:用于合并两个或多个SELECT语句的结果集。
- ORDER BY:用于根据一个或多个列对结果集进行排序。
- GROUP BY:用于对结果集进行分组,通常与聚合函数(如COUNT、MAX、MIN、SUM、AVG)一起使用。
- HAVING:用于对分组后的结果进行过滤。
- LIKE:用于在WHERE子句中搜索列中的特定模式。
- IN:用于在WHERE子句中指定多个值。
- BETWEEN:用于在WHERE子句中指定范围。
- EXISTS:用于检查子查询是否返回任何记录。
- DISTINCT:用于返回唯一不同的值。
- AS:用于为表或列指定别名。
- INDEX:用于创建索引,以提高查询性能。
- SELECT:用于从数据库中选择数据。基本语法是
- 这些是SQL的一些基础语法和命令,它们构成了大多数数据库操作的核心。
4-2 在Access中使用SQL
-
在创建菜单栏里找到
创建
,点击查询设计 -
这里我们选择切换SQL视图
-
输入SQL指令
-
双击查询1即出现结果
5 注册页面–数据库添加
5-1 用户输入预处理
- 我们打开
RegisterForm.cs
,我们需要在用户输入信息正确后进行数据库连接即可
5-1-1 确保学号是数字
int.TryParse
是 C# 中用于尝试将字符串转换为整数的方法。该方法原型如下:public static bool TryParse(string s, out int result);
s
: 要转换的字符串。result
: 如果转换成功,将包含转换后的整数值;如果转换失败,则为0
。
int.TryParse
方法返回一个布尔值,表示转换是否成功。如果字符串成功转换为整数,result
参数将包含该整数值,方法返回true
;如果转换失败(例如,字符串包含非数字字符或数字太大无法表示为int
),方法返回false
,并且result
参数保持不变。
private bool AreAllFieldsFilled()
{
if(string.IsNullOrEmpty(tbxID.Text)|| string.IsNullOrEmpty(tbxName.Text))
{
MessageBox.Show("请确认信息填写!");
return false;
}
if (!int.TryParse(tbxID.Text, out _))
{
MessageBox.Show("学号请填写数字!");
return false;
}
foreach (RadioButton radioButton in gbxGender.Controls)
{
if (radioButton.Checked)
{
return true;
}
}
MessageBox.Show("请选择性别!");
return false;
}
5-1-2 确保时间为日期
dTimePkBirth.Value
返回的数据为2024/8/22 21:05:54
- 而这里我们需要的是时间数据,
birthDate.Date
获取日期部分,忽略时间
DateTime birthDate = dTimePkBirth.Value;
DateTime birthDateOnly = birthDate.Date; // 获取日期部分,忽略时间
5-2 数据库连接
- 我们只需要调用单例模式的
GetInstance
接口即可(上面说过了)
StudentsDataBaseConnector studentsDataBaseConnector = StudentsDataBaseConnector.GetInstance();
OleDbConnection connection = studentsDataBaseConnector.GetStudentDataBaseConnection();
5-3 确保学号唯一—数据库搜索
- 同时我们需要确保注册的账户是唯一的,假定学号是所有学生唯一的标识符
- 这里我们用到这一条指令
SELECT COUNT(*) FROM StudentsLogin WHERE 学号 = @ID
-
SELECT
: 这是SQL查询语句的开始,用于指定要选择的操作。
COUNT(*)
: 这是一个聚合函数,用于计算选择的数据集中的记录数。*
表示选择所有的记录。FROM StudentsLogin
: 这指定了要查询的表名,这里是StudentsLogin
。WHERE
: 这是一个条件子句,用于过滤查询结果。只有满足WHERE
子句中条件的记录才会被包括在结果中。学号 = @ID
: 这是WHERE
子句中的条件,它指定了只有当学号
字段等于@ID
参数时,记录才会被计数。-SELECT
: 这是SQL查询语句的开始,用于指定要选择的操作。COUNT(*)
: 这是一个聚合函数,用于计算选择的数据集中的记录数。*
表示选择所有的记录。FROM StudentsLogin
: 这指定了要查询的表名,这里是StudentsLogin
。WHERE
: 这是一个条件子句,用于过滤查询结果。只有满足WHERE
子句中条件的记录才会被包括在结果中。学号 = @ID
: 这是WHERE
子句中的条件,它指定了只有当学号
字段等于@ID
参数时,记录才会被计数。
-
- 如果存在,就输出。
// 检查学号是否已存在
string checkIDSql = "SELECT COUNT(*) FROM StudentsLogin WHERE 学号 = @ID";
OleDbCommand checkIDCommand = new OleDbCommand(checkIDSql, connection);
checkIDCommand.Parameters.AddWithValue("@ID", int.Parse(ID));
int count = (int)checkIDCommand.ExecuteScalar();
if (count > 0)
{
MessageBox.Show("学号已存在,请重新输入!");
clearInputs();
return;
}
5-4 注册—数据库添加
- 我们为注册消息完成下述逻辑
INSERT INTO StudentsLogin (学号, 姓名, 性别,出生日期) VALUES (@ID, @Name, @Gender,@BirthDate)
-
INSERT INTO
: 这是SQL语句的开始,用于指定要执行的插入操作。
StudentsLogin
: 这是目标表的名称,即新记录将被插入的表。(学号, 姓名, 性别, 出生日期)
: 这指定了新记录将包含数据的列。这里的列名应该与StudentsLogin
表中实际的列名相匹配。VALUES (@ID, @Name, @Gender, @BirthDate)
: 这部分指定了要插入的值。@ID
、@Name
、@Gender
和@BirthDate
是参数占位符,它们将在执行SQL语句时被替换为实际的值。
-
- 然后我们添加参数
command.Parameters.AddWithValue("@ID", int.Parse(ID));
command.Parameters.AddWithValue("@Name", Name);
command.Parameters.AddWithValue("@Gender", gender);
command.Parameters.AddWithValue("@BirthDate", birthDateOnly);
- 完整代码
string register_data_info = $"将注册以下信息:\n学号: {ID}\n姓名: {Name}\n性别: {gender}\n出生日期: {birthDate.ToShortDateString()}";
MessageBox.Show(register_data_info);
clearInputs();
try
{
// 构造SQL INSERT语句
string sql = "INSERT INTO StudentsLogin (学号, 姓名, 性别,出生日期) VALUES (@ID, @Name, @Gender,@BirthDate)";
// 创建OleDbCommand对象
OleDbCommand command = new OleDbCommand(sql, connection);
// 添加参数并赋值
command.Parameters.AddWithValue("@ID", int.Parse(ID));
command.Parameters.AddWithValue("@Name", Name);
command.Parameters.AddWithValue("@Gender", gender);
command.Parameters.AddWithValue("@BirthDate", birthDateOnly);
// 执行SQL语句
command.ExecuteNonQuery();
MessageBox.Show("注册成功!");
}
catch (Exception ex)
{
// 处理异常
MessageBox.Show($"注册失败: {ex.Message}");
}
finally
{
// 关闭数据库连接
if (connection.State == ConnectionState.Open)
{
connection.Close();
}
}
5-5 注册页面完整代码
- RegisterForm.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Data.OleDb;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace StudentDatasetsExplorer
{
public partial class RegisterForm : Form
{
public RegisterForm()
{
InitializeComponent();
}
private void clearInputs()
{
tbxID.Clear();
tbxName.Clear();
foreach (RadioButton radioButton in gbxGender.Controls)
{
radioButton.Checked = false;
}
}
private bool AreAllFieldsFilled()
{
if(string.IsNullOrEmpty(tbxID.Text)|| string.IsNullOrEmpty(tbxName.Text))
{
MessageBox.Show("请确认信息填写!");
return false;
}
if (!int.TryParse(tbxID.Text, out _))
{
MessageBox.Show("学号请填写数字!");
return false;
}
foreach (RadioButton radioButton in gbxGender.Controls)
{
if (radioButton.Checked)
{
return true;
}
}
MessageBox.Show("请选择性别!");
return false;
}
private void btnRegister_Click(object sender, EventArgs e)
{
if (!AreAllFieldsFilled())
{
clearInputs();
return;
}
string ID =tbxID.Text;
string Name = tbxName.Text;
string gender = null;
foreach (RadioButton g in gbxGender.Controls)
{
if (g.Checked)
{
gender = g.Text;
}
}
DateTime birthDate = dTimePkBirth.Value;
DateTime birthDateOnly = birthDate.Date; // 获取日期部分,忽略时间
StudentsDataBaseConnector studentsDataBaseConnector = StudentsDataBaseConnector.GetInstance();
OleDbConnection connection = studentsDataBaseConnector.GetStudentDataBaseConnection();
// 检查学号是否已存在
string checkIDSql = "SELECT COUNT(*) FROM StudentsLogin WHERE 学号 = @ID";
OleDbCommand checkIDCommand = new OleDbCommand(checkIDSql, connection);
checkIDCommand.Parameters.AddWithValue("@ID", int.Parse(ID));
int count = (int)checkIDCommand.ExecuteScalar();
if (count > 0)
{
MessageBox.Show("学号已存在,请重新输入!");
clearInputs();
return;
}
string register_data_info = $"将注册以下信息:\n学号: {ID}\n姓名: {Name}\n性别: {gender}\n出生日期: {birthDate.ToShortDateString()}";
MessageBox.Show(register_data_info);
clearInputs();
try
{
// 构造SQL INSERT语句
string sql = "INSERT INTO StudentsLogin (学号, 姓名, 性别,出生日期) VALUES (@ID, @Name, @Gender,@BirthDate)";
// 创建OleDbCommand对象
OleDbCommand command = new OleDbCommand(sql, connection);
// 添加参数并赋值
command.Parameters.AddWithValue("@ID", int.Parse(ID));
command.Parameters.AddWithValue("@Name", Name);
command.Parameters.AddWithValue("@Gender", gender);
command.Parameters.AddWithValue("@BirthDate", birthDateOnly);
// 执行SQL语句
command.ExecuteNonQuery();
MessageBox.Show("注册成功!");
}
catch (Exception ex)
{
// 处理异常
MessageBox.Show($"注册失败: {ex.Message}");
}
finally
{
// 关闭数据库连接
if (connection.State == ConnectionState.Open)
{
connection.Close();
}
}
}
private void btnGoBack2Main_Click(object sender, EventArgs e)
{
//如果用户有填写部分信息,提示用户是否返回
if (!string.IsNullOrEmpty(tbxID.Text) ||
!string.IsNullOrEmpty(tbxName.Text) ||
gbxGender.Controls.OfType<RadioButton>().Any(rb => rb.Checked))
{
// 弹出确认对话框
DialogResult dialogResult = MessageBox.Show("您有未保存的信息,确定要返回吗?", "确认", MessageBoxButtons.YesNo);
if (dialogResult == DialogResult.Yes)
{
// 用户确认返回
clearInputs();
this.Close();
}
}else
{
this.Close();
}
}
}
}
6 登录页面
- 登录就是简单的数据查找,这里飞快过一下
StudentsDataBaseConnector studentsDataBaseConnector = StudentsDataBaseConnector.GetInstance();
OleDbConnection connection = studentsDataBaseConnector.GetStudentDataBaseConnection();
try
{
// 创建SQL查询命令
string sql = "SELECT COUNT(*) FROM Login WHERE 学号 =@ID AND 姓名 =@Name";
OleDbCommand command = new OleDbCommand(sql, connection);
// 添加参数并赋值
command.Parameters.AddWithValue("@ID", int.Parse(ID));
command.Parameters.AddWithValue("@Name", Name);
int count = (int)command.ExecuteScalar();
if (count > 0)
{
MessageBox.Show("登录成功!");
}
else
{
MessageBox.Show("学号不存在或姓名错误!");
}
}
catch (Exception ex)
{
MessageBox.Show("登录过程中发生错误: " + ex.Message);
}
finally
{
if (connection != null)
{
connection.Close();
}
}
6-2 登录页面完整代码
- LoginForm.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Data.OleDb;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace StudentDatasetsExplorer
{
public partial class LoginForm : Form
{
public LoginForm()
{
InitializeComponent();
}
private void btnLogin_Click(object sender, EventArgs e)
{
if (string.IsNullOrEmpty(tbxID.Text) || string.IsNullOrEmpty(tbxName.Text))
{
MessageBox.Show("请确认信息填写!");
tbxName.Clear();
tbxID.Clear();
return;
}
if (!int.TryParse(tbxID.Text, out _))
{
MessageBox.Show("学号请填写数字!");
tbxName.Clear();
tbxID.Clear();
return;
}
string Name=tbxName.Text;
string ID=tbxID.Text;
Console.WriteLine("ID:{0}, Name:{1}", ID, Name);
StudentsDataBaseConnector studentsDataBaseConnector = StudentsDataBaseConnector.GetInstance();
OleDbConnection connection = studentsDataBaseConnector.GetStudentDataBaseConnection();
try
{
// 创建SQL查询命令
string sql = "SELECT COUNT(*) FROM Login WHERE 学号 =@ID AND 姓名 =@Name";
OleDbCommand command = new OleDbCommand(sql, connection);
// 添加参数并赋值
command.Parameters.AddWithValue("@ID", int.Parse(ID));
command.Parameters.AddWithValue("@Name", Name);
int count = (int)command.ExecuteScalar();
if (count > 0)
{
MessageBox.Show("登录成功!");
}
else
{
MessageBox.Show("学号不存在或姓名错误!");
}
}
catch (Exception ex)
{
MessageBox.Show("登录过程中发生错误: " + ex.Message);
}
finally
{
if (connection != null)
{
connection.Close();
}
}
}
private void btnGoBack2Main_Click(object sender, EventArgs e)
{
DialogResult dialogResult = MessageBox.Show("您确定要退出登录吗?", "确认", MessageBoxButtons.YesNo);
if (dialogResult == DialogResult.Yes)
{
tbxName.Clear();
tbxID.Clear();
this.Close();
}
}
}
}
小结
- 本节讲述了如何通过VS2022连接Access并通过SQL实现登录和注册对数据库的查找和增加
- 下一期我们讲讲删除和修改的逻辑实现
- 感谢大家对本教程的支持,如有错误,欢迎指出!