
思维导图ADO.NET 编程的本质与优势定义ADO.NET 是 C# 中访问数据库的一套数据访问技术主要用于连接数据库、执行 SQL、读取结果以及完成增删改查操作。核心优势直接操作数据库可连接 SQL Server 等关系型数据库执行 SQL 语句。功能完整支持查询、插入、更新、删除、事务、参数化查询。性能较高底层控制更直接适合对数据库访问过程有明确要求的场景。适用范围广WinForm、ASP.NET、控制台程序都可以使用。为什么需要 ADO.NET在实际开发中程序中的数据通常不会只保存在内存里而是要存到数据库中。例如用户登录信息要存数据库学生成绩要从数据库读取订单数据要写入数据库后台管理系统要做增删改查ADO.NET 解决的是“C# 程序如何与数据库进行交互”的问题。ADO.NET 的核心对象Connection负责建立程序与数据库之间的连接。Command负责执行 SQL 语句或存储过程。DataReader只进只读方式读取数据效率高。DataAdapter在数据库和 DataSet 之间传递数据。DataSet / DataTable把数据缓存在内存中便于离线操作。ADO编程步骤编程步骤:创建连接对象编写sql创建DbCommand对象通知数据库执行sql:int ExecuteNonQuery() : 执行增删改的sqlobject ExecuteScalar(): 执行返回单行单列select语句DbDataReader ExcuteReader(): 执行select语句根据查询结果进行逻辑操作关闭资源ADO编程的流程最简单的理解就是先建立作为沟通桥梁的Sqlconnection然后编写sql语句再建立作为运输载具的DbCommand将sql语言装入DbCommand里面并指定对应的沟通桥梁SqlConnection之后选择相对应的执行方法就行了。需要注意的是sql语句不要使用拼接的写法不然容易被破解建议使用占位符。数据库连接using System; using System.Data.SqlClient; string connStr Server.;DatabaseSchoolDB;Integrated SecurityTrue; using SqlConnection conn new SqlConnection(connStr); conn.Open(); Console.WriteLine(数据库连接成功);说明SqlConnection 用于连接 SQL Server。Open() 打开连接使用完成后要及时释放。一般配合 using 使用避免连接泄漏。执行查询操作using System; using System.Data.SqlClient; string connStr Server.;DatabaseSchoolDB;Integrated SecurityTrue; using SqlConnection conn new SqlConnection(connStr); conn.Open(); string sql SELECT Id, Name, Age FROM Students; using SqlCommand cmd new SqlCommand(sql, conn); using SqlDataReader reader cmd.ExecuteReader(); while (reader.Read()) { Console.WriteLine(${reader[Id]} - {reader[Name]} - {reader[Age]}); }说明SqlCommand 表示要执行的 SQL。ExecuteReader() 用于执行查询并返回结果集。SqlDataReader 适合顺序读取大量数据。执行增删改操作using System; using System.Data.SqlClient; string connStr Server.;DatabaseSchoolDB;Integrated SecurityTrue; using SqlConnection conn new SqlConnection(connStr); conn.Open(); string sql UPDATE Students SET Age 20 WHERE Id 1; using SqlCommand cmd new SqlCommand(sql, conn); int rows cmd.ExecuteNonQuery(); Console.WriteLine($受影响行数{rows});说明ExecuteNonQuery() 常用于 INSERT、UPDATE、DELETE返回值是受影响的行数执行单个结果查询string sql SELECT COUNT(*) FROM Students; using SqlCommand cmd new SqlCommand(sql, conn); object result cmd.ExecuteScalar(); Console.WriteLine($学生总数{result});说明ExecuteScalar() 用于返回单个值常用于统计、最大值、最小值等查询参数化查询为了避免 SQL 注入实际开发中应优先使用参数化查询。string sql SELECT * FROM Students WHERE Name Name; using SqlCommand cmd new SqlCommand(sql, conn); cmd.Parameters.AddWithValue(Name, Alice); using SqlDataReader reader cmd.ExecuteReader(); while (reader.Read()) { Console.WriteLine(reader[Name]); }优势防止 SQL 注入提高安全性让 SQL 更清晰、可维护DataAdapter 和 DataSet如果希望把数据先读到内存里再操作可以使用 DataAdapter 和 DataSet。using System; using System.Data; using System.Data.SqlClient; string connStr Server.;DatabaseSchoolDB;Integrated SecurityTrue; using SqlConnection conn new SqlConnection(connStr); string sql SELECT * FROM Students; using SqlDataAdapter adapter new SqlDataAdapter(sql, conn); DataSet ds new DataSet(); adapter.Fill(ds, Students); foreach (DataRow row in ds.Tables[Students].Rows) { Console.WriteLine(${row[Id]} - {row[Name]}); }说明DataSet 类似内存中的小型数据库适合临时缓存、数据绑定、离线操作但相比 DataReader 占用内存更多ADO.NET 中的事务当多条 SQL 必须“要么都成功要么都失败”时可以使用事务。事务: 在同一个事务中一组sql(增删改)语句, 要就全部成功,要就全部失败事务四大特性: ACID原子性一致性隔离性持久性隔离级别比较using SqlConnection conn new SqlConnection(connStr); conn.Open(); SqlTransaction tran conn.BeginTransaction(); try { SqlCommand cmd1 new SqlCommand( UPDATE Accounts SET Balance Balance - 100 WHERE Id 1, conn, tran); SqlCommand cmd2 new SqlCommand( UPDATE Accounts SET Balance Balance 100 WHERE Id 2, conn, tran); cmd1.ExecuteNonQuery(); cmd2.ExecuteNonQuery(); tran.Commit(); } catch { tran.Rollback(); throw; }ADO封装正确使用封装可以提高代码的可读性、可维护性和可扩展性同时提供更好的控制数据和操作的方式。这是一种良好的编程实践有助于构建高质量的软件系统。如建立数据库辅助类DBHelper将sql操作分装。static string ConnStr Serverlocalhost;DatabaseSuperMarket;User Idsa;Password123456;; /// summary /// 执行查询操作返回SqlDataReader对象 /// /summary /// param namesql普通的SQL语句/param /// returns返回的SqlDataReader对象/returns public static SqlDataReader GetRead(string sql) { SqlConnection con new SqlConnection(ConnStr); try { con.Open(); SqlCommand com new SqlCommand(sql, con); SqlDataReader dr com.ExecuteReader(CommandBehavior.CloseConnection); return dr; } catch (Exception ex) { return null; } } /// summary /// 执行查询操作返回SqlDataReader对象(参数化SQL语句) /// /summary /// param namesql参数化SQL语句/param /// returns返回的SqlDataReader对象/returns public static SqlDataReader GetRead(string sql, ListSqlParameter parameters) { SqlConnection con new SqlConnection(ConnStr); try { con.Open(); SqlCommand com new SqlCommand(sql, con); com.Parameters.AddRange(parameters.ToArray()); SqlDataReader dr com.ExecuteReader(CommandBehavior.CloseConnection); return dr; } catch (Exception ex) { return null; } finally { } } /// summary /// 执行查询操作使用DataSet获取数据 /// /summary /// param namesql参数化sql语句/param /// param nameparameters/param /// returns返回DataSet/returns public static DataSet GetDataSet(string sql, ListSqlParameter parameters) { using (SqlConnection con new SqlConnection(ConnStr)) { using (SqlDataAdapter da new SqlDataAdapter(sql, con)) { da.SelectCommand.Parameters.AddRange(parameters.ToArray()); DataSet ds new DataSet(); da.Fill(ds); return ds; } } }数据库相对应的Dal类将sql语句分装/// summary /// 查询所有商品信息以及分类信息 /// /summary public void GetAllCommodityWithSort() { string sql select c.CommodityID, c.CommodityName, c.SortID, s.SortName, c.CommodityPrice, c.IsDiscount, c.ReducedPrice from [Commodity] c join [CommoditySort] s on c.SortID s.SortID; using (SqlDataReader reader DBHelper.GetRead(sql)) { while (reader.Read()) { Console.Write(商品编号 reader[CommodityID] ); Console.Write(商品名称 reader[CommodityName] ); Console.Write(分类编号 reader[SortID] ); Console.Write(分类名称 reader[SortName] ); Console.Write(商品价格 reader[CommodityPrice] ); Console.Write(是否打折 reader[IsDiscount] ); Console.Write(折后价格 reader[ReducedPrice] ); Console.WriteLine(); } reader.Close(); } } /// summary /// 根据商品编号查询商品信息以及分类信息 /// /summary /// param namecommodityId/param public void GetCommodityById(int commodityId) { string sql select c.CommodityID, c.CommodityName, c.SortID, s.SortName, c.CommodityPrice, c.IsDiscount, c.ReducedPrice from [Commodity] c join [CommoditySort] s on c.SortID s.SortID where c.CommodityIDCommodityID; ListSqlParameter sps new ListSqlParameter(); sps.Add(new SqlParameter(CommodityID, commodityId)); using (SqlDataReader reader DBHelper.GetRead(sql,sps)) { if (reader.Read()) { Console.Write(商品编号 reader[CommodityID] ); Console.Write(商品名称 reader[CommodityName] ); Console.Write(分类编号 reader[SortID] ); Console.Write(分类名称 reader[SortName] ); Console.Write(商品价格 reader[CommodityPrice] ); Console.Write(是否打折 reader[IsDiscount] ); Console.Write(折后价格 reader[ReducedPrice] ); Console.WriteLine(); } else { Console.WriteLine(没有找到该商品。); } reader.Close(); } } /// summary /// 根据商品名模糊查询商品信息以及分类信息 /// /summary /// param namecommodityName/param /// returns/returns public DataSet GetCommodityByName(string commodityName) { string sql select c.CommodityID, c.CommodityName, c.SortID, s.SortName, c.CommodityPrice, c.IsDiscount, c.ReducedPrice from [Commodity] c join [CommoditySort] s on c.SortID s.SortID where c.CommodityName like CommodityName; ListSqlParameter sps new ListSqlParameter(); sps.Add(new SqlParameter(CommodityName, % commodityName %)); return DBHelper.GetDataSet(sql, sps); }使用注意数据库连接要及时关闭优先使用 using。查询数据时DataReader 快但只能向前读取。需要缓存和离线处理时可使用 DataSet。实际开发中必须使用参数化查询避免 SQL 注入。涉及多步更新时建议使用事务保证数据一致性。面试题分享ADO.NET 中 ExecuteReader、ExecuteNonQuery、ExecuteScalar 的区别是什么DataReader 和 DataSet 有什么区别为什么参数化查询可以防止 SQL 注入using 在数据库编程中有什么作用事务的4大特性是什么