深入理解 ADO.NET 数据访问技术与实用工具类
ADO.NET 核心架构解析
ADO.NET 是 .NET 平台中用于与数据源交互的核心组件,它提供了一套完整的数据访问机制,支持关系型数据库(如 SQL Server、Oracle)和非关系型数据源的连接与操作。其设计兼顾高性能与灵活性,广泛应用于桌面、Web 和服务端开发。
关键组件详解
1. Connection 对象:建立数据库通信
Connection 类负责管理应用程序与数据库之间的连接。通过配置连接字符串(ConnectionString),可指定服务器地址、数据库名、身份验证方式等信息。
string connStr = "Server=localhost;Database=TestDB;User Id=sa;Password=123456;";
using (SqlConnection conn = new SqlConnection(connStr))
{
conn.Open();
// 执行后续操作
}
2. Command 对象:执行 SQL 指令
Command 用于向数据库发送 T-SQL 命令,支持增删改查及存储过程调用。常用方法包括:
- ExecuteNonQuery():执行非查询语句(INSERT/UPDATE/DELETE),返回受影响行数。
- ExecuteScalar():返回结果集中第一行第一列的值,常用于聚合函数查询。
- ExecuteReader():获取只读、前向的数据流,适合处理大量记录。
3. DataReader:高效读取数据流
DataReader 提供快速、只进、只读的数据访问模式,占用内存小,适用于不需要缓存或随机访问的场景。
using (SqlCommand cmd = new SqlCommand("SELECT Name, Age FROM Users", conn))
using (SqlDataReader reader = cmd.ExecuteReader())
{
while (reader.Read())
{
string name = reader.GetString("Name");
int age = reader.GetInt32("Age");
Console.WriteLine($"{name}, {age}");
}
}
4. DataAdapter 与 DataSet:离线数据操作
DataAdapter 充当数据库与 DataSet 之间的桥梁,通过 Fill 方法将查询结果加载到 DataSet 中。DataSet 是内存中的数据容器,支持多表、关系、约束,并可在断开连接状态下进行增删改操作,最后通过 Update 方法同步回数据库。
通用数据库助手类实现
以下是一个封装了常见数据库操作的辅助类,支持事务管理、参数化查询以及动态 SQL 构建。
查看 DbHelper 实现代码
public sealed class DatabaseHelper : IDisposable
{
private DbConnection _connection;
private DbTransaction _transaction;
private DbProviderFactory _factory;
public DatabaseHelper(string connectionString, string providerName)
{
_factory = DbProviderFactories.GetFactory(providerName);
_connection = _factory.CreateConnection();
_connection.ConnectionString = connectionString;
}
public void OpenConnection()
{
if (_connection.State != ConnectionState.Open)
_connection.Open();
}
public void BeginTransaction()
{
OpenConnection();
_transaction = _connection.BeginTransaction();
}
public void Commit()
{
_transaction?.Commit();
_transaction = null;
}
public void Rollback()
{
_transaction?.Rollback();
_transaction = null;
}
public int ExecuteNonQuery(string sql, params DbParameter[] parameters)
{
using var command = CreateCommand(sql, parameters);
return command.ExecuteNonQuery();
}
public object ExecuteScalar(string sql, params DbParameter[] parameters)
{
using var command = CreateCommand(sql, parameters);
return command.ExecuteScalar() ?? null;
}
public DbDataReader ExecuteReader(string sql, params DbParameter[] parameters)
{
var command = CreateCommand(sql, parameters);
return command.ExecuteReader(CommandBehavior.CloseConnection);
}
public DataTable ExecuteDataTable(string sql, params DbParameter[] parameters)
{
using var command = CreateCommand(sql, parameters);
using var adapter = _factory.CreateDataAdapter();
adapter.SelectCommand = command;
var table = new DataTable();
adapter.Fill(table);
return table;
}
public DataSet ExecuteDataSet(string sql, params DbParameter[] parameters)
{
using var command = CreateCommand(sql, parameters);
using var adapter = _factory.CreateDataAdapter();
adapter.SelectCommand = command;
var dataset = new DataSet();
adapter.Fill(dataset);
return dataset;
}
private DbCommand CreateCommand(string sql, DbParameter[] parameters)
{
var cmd = _connection.CreateCommand();
cmd.CommandText = sql;
cmd.Transaction = _transaction;
if (parameters != null)
{
foreach (var param in parameters)
cmd.Parameters.Add(param);
}
return cmd;
}
public void Dispose()
{
_transaction?.Dispose();
_connection?.Dispose();
_transaction = null;
_connection = null;
}
}
SQL Server 专用操作封装
针对 SQL Server 的高频操作,可进一步封装为静态工具类,简化调用流程。
查看 SqlHelper 实现代码
public static class SqlDataAccess
{
public static string ConnectionString { get; set; }
public static int Execute(string sql, params SqlParameter[] parameters)
{
using var conn = new SqlConnection(ConnectionString);
using var cmd = new SqlCommand(sql, conn);
if (parameters != null)
cmd.Parameters.AddRange(parameters);
conn.Open();
return cmd.ExecuteNonQuery();
}
public static object GetScalar(string sql, params SqlParameter[] parameters)
{
using var conn = new SqlConnection(ConnectionString);
using var cmd = new SqlCommand(sql, conn);
if (parameters != null)
cmd.Parameters.AddRange(parameters);
conn.Open();
var result = cmd.ExecuteScalar();
return result == DBNull.Value ? null : result;
}
public static SqlDataReader Read(string sql, params SqlParameter[] parameters)
{
var conn = new SqlConnection(ConnectionString);
var cmd = new SqlCommand(sql, conn);
if (parameters != null)
cmd.Parameters.AddRange(parameters);
conn.Open();
return cmd.ExecuteReader(CommandBehavior.CloseConnection);
}
public static DataTable QueryTable(string sql, params SqlParameter[] parameters)
{
using var conn = new SqlConnection(ConnectionString);
using var cmd = new SqlCommand(sql, conn);
if (parameters != null)
cmd.Parameters.AddRange(parameters);
using var adapter = new SqlDataAdapter(cmd);
var table = new DataTable();
adapter.Fill(table);
return table;
}
public static void ExecuteInTransaction(Dictionary<string, SqlParameter[]> sqlCommands)
{
using var conn = new SqlConnection(ConnectionString);
conn.Open();
using var trans = conn.BeginTransaction();
try
{
foreach (var entry in sqlCommands)
{
using var cmd = new SqlCommand(entry.Key, conn, trans);
if (entry.Value != null)
cmd.Parameters.AddRange(entry.Value);
cmd.ExecuteNonQuery();
}
trans.Commit();
}
catch
{
trans.Rollback();
throw;
}
}
}
最佳实践建议
- 始终使用参数化查询防止 SQL 注入攻击。
- 利用
using语句确保连接和资源正确释放。 - 对于大数据量读取优先考虑 DataReader;需要离线操作时使用 DataSet/DataTable。
- 事务操作应尽量短小,避免长时间锁定资源。