DatabaseHelper类:原理与实践
1.DatabaseHelper代码
using System;
using System.Data;
using System.Data.SqlClient;
using System.Threading.Tasks;
public class DatabaseHelper
{
// 数据库连接字符串,它包含了服务器位置、数据库名、认证信息等。
// 原理:这是一个标准化的字符串,定义了如何找到并连接到数据库。
private readonly string _connectionString;
// 构造方法:必须传入一个有效的连接字符串来实例化这个类。
// 原理:构造函数是类实例化时的初始化点,这里用来保证每个DatabaseHelper实例都有一个有效的连接字符串。
public DatabaseHelper(string connectionString)
{
// 如果传入的连接字符串为空,那就抛出一个异常,提醒开发者这是不允许的。
_connectionString = connectionString ?? throw new ArgumentNullException(nameof(connectionString), "数据库连接字符串不能为空!");
}
// 异步执行查询并返回一个DataTable。
// 原理:异步执行允许方法调用返回到调用者而不等待任务完成,这样不会阻塞调用线程。
public async Task<DataTable> ExecuteQueryAsync(string sql, CommandType commandType = CommandType.Text, SqlParameter[] parameters = null)
{
// 使用using语句确保资源被正确释放。
// 原理:using声明会在结束时自动调用Dispose方法,确保连接对象被正确关闭,防止资源泄露。
await using var conn = new SqlConnection(_connectionString);
await conn.OpenAsync(); // 异步打开数据库连接。
// 初始化SqlCommand并设置必要的属性。
// 原理:SqlCommand对象负责把SQL指令传递给数据库执行。
await using var cmd = new SqlCommand(sql, conn) { CommandType = commandType };
if (parameters != null)
cmd.Parameters.AddRange(parameters); // 添加参数以防止SQL注入。
// SqlDataAdapter负责执行SqlCommand并填充DataTable。
// 原理:DataAdapter充当了数据库和DataTable之间的桥梁,将数据映射进内存中的对象。
await using var adapter = new SqlDataAdapter(cmd);
var dataTable = new DataTable(); // 创建DataTable用于存放结果集。
adapter.Fill(dataTable); // 填充DataTable。
return dataTable; // 返回填充好的DataTable。
}
// 异步执行非查询SQL命令(如INSERT、UPDATE、DELETE)。
// 原理:这类操作通常会改变数据库中的数据,但不需要返回数据。
public async Task<int> ExecuteNonQueryAsync(string sql, CommandType commandType = CommandType.Text, SqlParameter[] parameters = null)
{
await using var conn = new SqlConnection(_connectionString);
await conn.OpenAsync(); // 异步打开连接。
// 初始化SqlCommand并设置必要的属性。
// 原理:SqlCommand通过ExecuteNonQueryAsync执行SQL语句,返回受影响的行数,这是对数据库修改操作的确认。
await using var cmd = new SqlCommand(sql, conn) { CommandType = commandType };
if (parameters != null)
cmd.Parameters.AddRange(parameters); // 添加参数以防止SQL注入。
// 执行非查询SQL命令并返回影响的行数。
return await cmd.ExecuteNonQueryAsync();
}
// 这个类可以根据需要扩展,比如添加事务处理、批量操作等高级功能。
}
2.原理讲解:
-
异步操作原理:通过使用
async
和await
关键字,我们可以让数据库操作在一个独立的线程上进行,不会"冻结"用户界面或是阻塞当前执行的线程。这就允许用户在长时间操作(如从数据库检索大量数据)进行时继续与应用程序交互。 -
SqlConnection原理:
SqlConnection
对象是ADO.NET中用于与SQL Server数据库建立连接的类。当调用OpenAsync
方法时,它在后台与数据库服务器建立连接,这个连接可以被用于之后的所有数据库操作。 -
SqlCommand原理:
SqlCommand
对象用于执行SQL语句或者存储过程,并返回结果。我们可以通过设置它的CommandType
属性来定义我们要执行的是一个文本指令(SQL语句)还是一个存储过程。 -
SqlParameter原理:参数化查询是一种防止SQL注入攻击的方法。通过使用
SqlParameter
,我们可以把外部输入作为参数传递给SQL命令,而不是直接拼接在SQL语句中,从而避免恶意输入可能导致的安全问题。 -
SqlDataAdapter和DataTable原理:
SqlDataAdapter
充当了一个中介,它执行SqlCommand
并将结果集填充到DataTable
对象中。DataTable
是一个内存中的数据表示,可以被用于读取和操作数据。
3.使用案例
我们已经有了一个功能齐全并且注释详尽的DatabaseHelper
类,现在让我们来编写一些使用案例,同时穿插一些幽默的元素和原理解释,确保既能学习到实际应用,又不会枯燥。
使用案例1:读取数据
假设你想要从数据库中读取一些用户信息,你可能会有一个像这样的SQL查询语句:“SELECT * FROM Users”。让我们用DatabaseHelper
来执行它,并处理结果。
// 实例化DatabaseHelper。别忘了,连接字符串就像你家的WIFI密码,不能弄错,也不能告诉别人。
var databaseHelper = new DatabaseHelper("你的连接字符串");
// 异步查询所有用户数据,就像是网购一样,点击查询后可以继续逛其他的东西。
var dataTable = await databaseHelper.ExecuteQueryAsync("SELECT * FROM Users");
foreach (DataRow row in dataTable.Rows)
{
// 逐行打印用户信息,就好比在叫号,准备领取大米。
Console.WriteLine($"用户ID: {row["Id"]}, 用户名: {row["Username"]}");
}
这里的原理是,通过ExecuteQueryAsync
方法,我们发送了一个SQL查询到数据库。它返回一个DataTable
,这个DataTable
包括了所有匹配查询条件的数据。我们用foreach
循环遍历DataTable
中的每一DataRow
,就像数星星一样,但好在这里的星星(数据行)是有限的。
使用案例2:添加新数据
现在,假设我们要向数据库中添加一个新用户,我们的SQL语句可能是"INSERT INTO Users (Username, Password) VALUES (@Username, @Password)"。我们将使用参数化查询来处理这个操作。
// 再次实例化DatabaseHelper
var databaseHelper = new DatabaseHelper("你的连接字符串");
// 创建参数化查询的参数
SqlParameter[] parametersToAddUser = new SqlParameter[]
{
// 我们用参数化查询来防止恶意的SQL注入,就像用锁链锁好自行车一样。
new SqlParameter("@Username", SqlDbType.VarChar) { Value = "新用户"},
new SqlParameter("@Password", SqlDbType.VarChar) { Value = "超安全的密码123"}
};
// 异步执行SQL命令,添加新用户到数据库中,就像是网上提交订单。
int rowsAffected = await databaseHelper.ExecuteNonQueryAsync(
"INSERT INTO Users (Username, Password) VALUES (@Username, @Password)",
CommandType.Text, parametersToAddUser);
Console.WriteLine($"插入了 {rowsAffected} 行数据。"); // 告诉我们有多少数据被成功插入,就像是确认收货一样。
在这个案例中,我们使用了参数化查询,这是因为我们不想让任何坏蛋通过SQL注入来破坏我们的数据库,就像是我们不会让陌生人随意进入我们的房间。通过定义SqlParameter
数组,并将其传递给ExecuteNonQueryAsync
方法,我们确保了我们的SQL命令既安全又容易理解。
使用案例3:更新数据
让我们来更新一个用户的密码。在现实生活中,这可能是因为用户忘记了密码,但在我们的例子中,这只是因为我们想展示一下这个工具类的魅力。
// 你懂的,每次操作前都得实例化DatabaseHelper
var databaseHelper = new DatabaseHelper("你的连接字符串");
// 参数化查询参数,为了用户的账户安全
SqlParameter[] parametersToUpdatePassword = new SqlParameter[]
{
new SqlParameter("@UserId", SqlDbType.Int) { Value = 1 },
new SqlParameter("@NewPassword", SqlDbType.VarChar) { Value = "新的超安全的密码456"}
};
// 异步更新用户密码
int rowsAffected = await databaseHelper.ExecuteNonQueryAsync(
"UPDATE Users SET Password = @NewPassword WHERE Id = @UserId",
CommandType.Text, parametersToUpdatePassword);
Console.WriteLine($"更新了 {rowsAffected} 行数据。"); // 就像是告诉用户他的密码已经被成功地重置。