🔥关注墨瑾轩,带你探索编程的奥秘!🚀
🔥超萌技术攻略,轻松晋级编程高手🚀
🔥技术宝库已备好,就等你来挖掘🚀
🔥订阅墨瑾轩,智趣学习不孤单🚀
🔥即刻启航,编程之旅更有趣🚀
当ADO.NET遇上“数据库江湖”
“各位看官,今天咱们不聊武侠小说,来说说现代程序员的“数据库江湖”——ADO.NET!想象你的代码像“跑酷达人”,却因为不会用ADO.NET而卡在数据库门口?那可真是‘拿着算盘玩量子计算机’——太Low啦!”
从“小白”到“大神”的修炼手册
一、ADO.NET基础:连接数据库的“武林秘籍”
1. 核心组件初探
// 创建SQL Server连接(C#示例)
using System;
using System.Data.SqlClient;
public class DatabaseConnection {
public void ConnectToDatabase() {
string connectionString = "Server=myServerAddress;Database=myDataBase;User Id=myUsername;Password=myPassword;"; // 连接字符串
using (SqlConnection connection = new SqlConnection(connectionString)) { // 使用using自动释放资源
try {
connection.Open(); // 打开连接
Console.WriteLine("数据库连接成功!");
} catch (SqlException ex) {
Console.WriteLine("连接失败:" + ex.Message); // 异常处理
}
}
}
}
代码注释解析:
SqlConnection
是连接数据库的“钥匙”,connectionString
是“密码本”。using
语句确保资源自动释放,避免内存泄漏。try-catch
是程序员的“护甲”,防止程序崩溃。
2. 小故事:某程序员第一次用ADO.NET时,把Password
写成了PassWord
,结果数据库死活连不上…(教训:细节决定成败!)
二、数据操作:从“只读”到“增删改”的“七十二变”
1. 查询数据(DataReader篇)
// 使用SqlDataReader读取数据
public void ReadData() {
string query = "SELECT * FROM Users";
using (SqlConnection connection = new SqlConnection(connectionString)) {
SqlCommand command = new SqlCommand(query, connection); // 创建命令
try {
connection.Open();
SqlDataReader reader = command.ExecuteReader(); // 执行查询
while (reader.Read()) { // 逐行读取
Console.WriteLine($"ID: {reader["ID"]}, Name: {reader["Name"]}"); // 输出数据
}
} catch (SqlException ex) {
Console.WriteLine("读取失败:" + ex.Message);
}
}
}
专业建议:
SqlDataReader
是“只读游标”,适合快速读取大量数据。reader.Read()
是“向前走”的逻辑,不能回头哦!
2. 插入数据(Command篇)
// 插入新用户
public void InsertUser(string name, int age) {
string query = "INSERT INTO Users (Name, Age) VALUES (@Name, @Age)"; // 参数化查询
using (SqlConnection connection = new SqlConnection(connectionString)) {
SqlCommand command = new SqlCommand(query, connection);
command.Parameters.AddWithValue("@Name", name); // 绑定参数
command.Parameters.AddWithValue("@Age", age);
try {
connection.Open();
int rowsAffected = command.ExecuteNonQuery(); // 执行插入
Console.WriteLine($"{rowsAffected} 行已插入。");
} catch (SqlException ex) {
Console.WriteLine("插入失败:" + ex.Message);
}
}
}
代码注释解析:
@Name
和@Age
是参数占位符,防止SQL注入。ExecuteNonQuery()
用于增删改操作,返回受影响行数。
3. 小故事:某程序员忘记用参数化查询,直接拼接SQL,结果被黑客“黑”了数据库…(教训:永远别信用户输入!)
三、DataAdapter与DataSet:离线操作的“乾坤大挪移”
1. 数据适配器(DataAdapter篇)
// 使用DataAdapter填充DataSet
public void LoadDataWithDataAdapter() {
string query = "SELECT * FROM Products";
using (SqlConnection connection = new SqlConnection(connectionString)) {
SqlDataAdapter adapter = new SqlDataAdapter(query, connection); // 创建适配器
DataSet dataSet = new DataSet(); // 离线数据集
try {
adapter.Fill(dataSet, "Products"); // 填充数据
DataTable productsTable = dataSet.Tables["Products"];
foreach (DataRow row in productsTable.Rows) {
Console.WriteLine($"ProductID: {row["ProductID"]}, Name: {row["ProductName"]}"); // 输出数据
}
} catch (SqlException ex) {
Console.WriteLine("加载失败:" + ex.Message);
}
}
}
代码注释解析:
DataAdapter
是“桥梁”,负责在数据库和DataSet
之间传递数据。DataSet
是“离线数据库”,适合复杂操作(如排序、筛选)。
2. 更新数据(DataAdapter篇)
// 更新数据并提交回数据库
public void UpdateDataWithDataAdapter() {
string query = "SELECT * FROM Products";
using (SqlConnection connection = new SqlConnection(connectionString)) {
SqlDataAdapter adapter = new SqlDataAdapter(query, connection);
SqlCommandBuilder builder = new SqlCommandBuilder(adapter); // 自动生成更新命令
DataSet dataSet = new DataSet();
adapter.Fill(dataSet, "Products");
DataTable productsTable = dataSet.Tables["Products"];
// 修改第一行数据
if (productsTable.Rows.Count > 0) {
DataRow row = productsTable.Rows[0];
row["Price"] = 99.99; // 修改价格
row.SetModified(); // 标记为已修改
}
try {
adapter.Update(dataSet, "Products"); // 提交更改
Console.WriteLine("数据已更新。");
} catch (SqlException ex) {
Console.WriteLine("更新失败:" + ex.Message);
}
}
}
专业建议:
SqlCommandBuilder
是“自动翻译官”,根据DataAdapter
生成UPDATE
命令。SetModified()
是关键,否则DataAdapter
不会提交更改。
四、事务处理:从“单打独斗”到“团队协作”
1. 事务管理(Transaction篇)
// 事务处理示例
public void PerformTransaction() {
string query1 = "UPDATE Accounts SET Balance = Balance - 100 WHERE AccountID = 1";
string query2 = "UPDATE Accounts SET Balance = Balance + 100 WHERE AccountID = 2";
using (SqlConnection connection = new SqlConnection(connectionString)) {
connection.Open();
SqlTransaction transaction = connection.BeginTransaction(); // 开始事务
try {
SqlCommand command = connection.CreateCommand();
command.Transaction = transaction; // 关联事务
command.CommandText = query1;
command.ExecuteNonQuery(); // 执行第一条SQL
command.CommandText = query2;
command.ExecuteNonQuery(); // 执行第二条SQL
transaction.Commit(); // 提交事务
Console.WriteLine("转账成功!");
} catch (SqlException ex) {
transaction.Rollback(); // 回滚事务
Console.WriteLine("转账失败:" + ex.Message);
}
}
}
代码注释解析:
SqlTransaction
是“原子操作”,要么全成功,要么全失败。Commit()
是“确认”,Rollback()
是“撤销”。
2. 小故事:某程序员忘记提交事务,结果用户的钱一直“卡在半路”…(教训:事务必须显式提交!)
五、性能优化:从“卡顿地狱”到“丝滑天堂”
1. 异步操作(Async/Await篇)
// 异步查询数据
public async Task ReadDataAsync() {
string query = "SELECT * FROM Logs";
using (SqlConnection connection = new SqlConnection(connectionString)) {
SqlCommand command = new SqlCommand(query, connection);
try {
await connection.OpenAsync(); // 异步打开连接
SqlDataReader reader = await command.ExecuteReaderAsync(); // 异步执行查询
while (await reader.ReadAsync()) { // 异步读取
Console.WriteLine($"LogID: {reader["LogID"]}, Message: {reader["Message"]}");
}
} catch (SqlException ex) {
Console.WriteLine("异步读取失败:" + ex.Message);
}
}
}
专业建议:
async/await
是“非阻塞魔法”,让程序更流畅。OpenAsync()
和ExecuteReaderAsync()
是异步操作的“标配”。
2. 连接池配置(Connection Pooling篇)
// 优化连接池配置
string optimizedConnectionString = "Server=myServerAddress;Database=myDataBase;" +
"User Id=myUsername;Password=myPassword;" +
"Pooling=true;Min Pool Size=5;Max Pool Size=100;"; // 配置连接池
代码注释解析:
Pooling=true
启用连接池,减少频繁创建连接的开销。Min Pool Size
和Max Pool Size
控制连接池的“最小兵”和“最大兵”。
六、高级技巧:从“普通玩家”到“隐藏大佬”
1. 参数化查询进阶
// 动态构建参数化查询
public void SearchUsers(string nameFilter, int minAge) {
string query = "SELECT * FROM Users WHERE 1=1"; // 1=1是占位符
List<SqlParameter> parameters = new List<SqlParameter>();
if (!string.IsNullOrEmpty(nameFilter)) {
query += " AND Name LIKE @Name"; // 动态添加条件
parameters.Add(new SqlParameter("@Name", $"%{nameFilter}%"));
}
if (minAge > 0) {
query += " AND Age >= @MinAge";
parameters.Add(new SqlParameter("@MinAge", minAge));
}
using (SqlConnection connection = new SqlConnection(connectionString)) {
SqlCommand command = new SqlCommand(query, connection);
command.Parameters.AddRange(parameters.ToArray()); // 添加参数
try {
connection.Open();
SqlDataReader reader = command.ExecuteReader();
while (reader.Read()) {
Console.WriteLine($"ID: {reader["ID"]}, Name: {reader["Name"]}, Age: {reader["Age"]}");
}
} catch (SqlException ex) {
Console.WriteLine("搜索失败:" + ex.Message);
}
}
}
小技巧:
1=1
是SQL中的“万能开关”,方便动态拼接条件。
2. LINQ to ADO.NET(简化查询)
// 使用LINQ查询DataSet
public void QueryWithLinq() {
string query = "SELECT * FROM Products";
using (SqlConnection connection = new SqlConnection(connectionString)) {
SqlDataAdapter adapter = new SqlDataAdapter(query, connection);
DataSet dataSet = new DataSet();
adapter.Fill(dataSet, "Products");
var expensiveProducts = from row in dataSet.Tables["Products"].AsEnumerable()
where row.Field<decimal>("Price") > 100
select row;
foreach (var product in expensiveProducts) {
Console.WriteLine($"Expensive Product: {product["ProductName"]}, Price: {product["Price"]}");
}
}
}
专业建议:LINQ是“SQL的C#版”,让代码更简洁优雅。
七、异常处理:从“手忙脚乱”到“泰山压顶不弯腰”
1. 多层异常捕获
// 多层异常处理示例
public void SafeDatabaseOperation() {
try {
// 数据库操作
} catch (SqlException ex) {
if (ex.Number == 2627) { // 主键冲突
Console.WriteLine("主键冲突,请检查输入数据。");
} else if (ex.Number == 547) { // 外键约束
Console.WriteLine("外键约束错误,关联数据不存在。");
} else {
Console.WriteLine("数据库错误:" + ex.Message);
}
} catch (Exception ex) {
Console.WriteLine("通用错误:" + ex.Message);
} finally {
// 清理资源
}
}
小故事:某程序员用
catch (Exception)
抓了一堆异常,结果发现是网络问题…(教训:具体异常优先!)
结论:从“菜鸟”到“大神”的蜕变
最后分享个趣事:某程序员第一次用
DataAdapter
时,把Fill
方法写成了Full
,结果数据一直没加载…(教训:拼写要仔细!)
互动提问:你在使用ADO.NET时遇到过哪些“按下葫芦浮起瓢”的趣事?欢迎在评论区分享你的江湖故事!