一、基础题
1、ADO.NET中读写数据库需要使用哪些对象?作用是什么?
SqlConnection 用于建立和管理与数据库的连接
SqlCommand 执行命令和存储过程
SqlDataReader 向前只读的数据流
SqlDataAdapter 适配器,支持增删改查,用于填充DataSet
DataSet 在内存中的存储器,支持断开连接的数据操作
2、什么是 ADO.NET?它的主要组成部分有哪些?
ADO.NET 是 .NET 框架中的一个组件,提供了与数据库进行通信的功能。
3、DataSet 和 DataReader 有什么区别?在什么情况下使用 DataSet,什么情况下使用 DataReader?
DataReader 是一个只读、只进的数据流,适合高性能、一次性读取数据的场景,例如快速读取查询结果。
DataSet 是一个内存中的数据存储,它可以处理多个表和关系,适合需要离线操作、批量数据处理的场景。
4、描述 ADO.NET 中的连接池(Connection Pooling)。如何配置它?
连接池 是一个数据库连接缓存机制,避免频繁地打开和关闭数据库连接,提高性能。连接池可以通过连接字符串中的参数进行配置,例如:
Min Pool Size
:最小连接数。Max Pool Size
:最大连接数。Pooling
:启用或禁用连接池(默认启用)
5、ADO.NET 中的 Command 对象有什么作用?它的常用方法有哪些?
- SqlCommand 对象 用于执行 SQL 语句或存储过程。常用方法包括:
ExecuteNonQuery()
:执行 SQL 语句(INSERT, UPDATE, DELETE),返回受影响的行数。ExecuteScalar()
:执行查询并返回结果集中的第一行第一列。ExecuteReader()
:执行查询并返回一个 DataReader 对象用于读取结果集。
6、什么是 Parameterized Queries?为什么在 ADO.NET 中建议使用它?
Parameterized Queries 是通过使用参数来避免 SQL 注入攻击,并提高查询的可读性和重用性。例如:
cs
SqlCommand command = new SqlCommand("SELECT * FROM Users WHERE UserID = @UserID", connection);
command.Parameters.AddWithValue("@UserID", userId);
7、ADO.NET 中的 DataAdapter 是什么?它与 DataSet 的关系是什么?
SqlDataAdapter 是连接数据库和 DataSet 的桥梁。它使用Command
对象从数据库中检索数据,并填充到DataSet
中。同时,它还可以将DataSet
中的更改(如插入、更新、删除)提交回数据库。
8、如何在 ADO.NET 中处理存储过程?请描述步骤并提供代码示例。
步骤:
- 创建 SqlCommand 对象,设置 CommandType 为
CommandType.StoredProcedure
。 - 添加存储过程所需的参数。
- 打开数据库连接,执行命令。
- 读取返回的结果(如果有)。
示例代码:
cs
using (SqlCommand command = new SqlCommand("GetUserById", connection))
{
command.CommandType = CommandType.StoredProcedure;
command.Parameters.AddWithValue("@UserID", userId);
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
Console.WriteLine(reader["Name"].ToString());
}
}
}
9、如何使用 ADO.NET 实现批量数据插入?
可以使用 SqlBulkCopy
类进行批量插入,它可以高效地将大批量数据插入到 SQL Server 数据库中。
cs
using (SqlBulkCopy bulkCopy = new SqlBulkCopy(connection))
{
bulkCopy.DestinationTableName = "Users";
bulkCopy.WriteToServer(dataTable); // dataTable 是包含要插入数据的 DataTable 对象
}
10、在 ADO.NET 中,如何处理数据库连接超时和异常?
使用 try-catch
来捕获异常,设置 SqlCommand
的 CommandTimeout
属性来处理超时。
cs
try
{
SqlCommand command = new SqlCommand("YourQuery", connection);
command.CommandTimeout = 30; // 设置超时时间为30秒
command.ExecuteNonQuery();
}
catch (SqlException ex)
{
Console.WriteLine("SQL Error: " + ex.Message);
}
catch (Exception ex)
{
Console.WriteLine("General Error: " + ex.Message);
}
11、如何使用 ADO.NET 实现分页查询?请写出一个示例。
可以使用 SQL Server 的 OFFSET-FETCH
子句来实现分页查询
cs
int pageNumber = 1;
int pageSize = 10;
string query = "SELECT * FROM Users ORDER BY UserID OFFSET @Offset ROWS FETCH NEXT @PageSize ROWS ONLY";
using (SqlCommand command = new SqlCommand(query, connection))
{
command.Parameters.AddWithValue("@Offset", (pageNumber - 1) * pageSize);
command.Parameters.AddWithValue("@PageSize", pageSize);
using (SqlDataReader reader = command.ExecuteReader())
{
while (reader.Read())
{
Console.WriteLine(reader["Name"].ToString());
}
}
}
12、如何在 ADO.NET 中使用异步编程(Async/Await)?请提供示例代码
使用异步方法如 ExecuteNonQueryAsync
、ExecuteReaderAsync
等来执行异步操作。
cs
public async Task<int> InsertUserAsync(string name, int age)
{
using (SqlCommand command = new SqlCommand("INSERT INTO Users (Name, Age) VALUES (@Name, @Age)", connection))
{
command.Parameters.AddWithValue("@Name", name);
command.Parameters.AddWithValue("@Age", age);
return await command.ExecuteNonQueryAsync();
}
}
13、如何优化 ADO.NET 的数据访问性能?请提供一些实用建议。
- 使用连接池以减少连接开销。
- 使用
DataReader
进行只读和只进的数据访问。 - 使用
SqlBulkCopy
进行批量数据操作。 - 避免在循环中频繁打开和关闭连接。
- 使用存储过程以减少网络开销。
14、在 ADO.NET 中,如何处理并发问题?
- 可以使用乐观并发控制,通过检查行版本号或时间戳来确保没有数据冲突。
- 也可以使用悲观并发控制,通过在读取数据时锁定记录来避免其他事务的修改。
15、什么是 Entity Framework?与 ADO.NET 相比,它有什么优势和劣势?
Entity Framework (EF) 是一个 ORM(对象关系映射)框架,它将数据库表映射到对象模型。
EF 的优势包括:
- 支持模型的自动生成和数据库的自动迁移。
- 支持 LINQ 查询语法。
- 简化了数据访问代码的编写。
劣势包括:
- 性能可能低于手写的 ADO.NET 代码,特别是在复杂查询的情况下。
- 对于非常复杂的查询,可能难以表达或调优。
16、解释 ADO.NET 中的连接泄漏问题是什么?如何避免这种情况?
连接泄漏 是指未正确关闭数据库连接,导致连接池耗尽和性能问题。避免连接泄漏的方法包括:
- 确保在
finally
块中关闭连接或使用using
语句来关闭连接。 - 使用连接池,并配置合适的最大连接数。
17、请解释ADO.NET中的连接池机制,并说明其重要性
连接池是ADO.NET中用于管理数据库连接的一个优化技术。当应用程序打开数据库连接时,连接池会检查是否有可用的现有连接(即之前关闭但尚未释放的连接)。如果有,它会重用该连接而不是创建一个新连接。这有助于减少连接开销,提高应用程序性能。
二、场景题
1、假设你需要从一个大型数据库表中获取数据并显示在应用程序中,你会如何设计数据访问层?
可以使用分页查询来限制每次获取的数据量,并使用 DataReader
进行高效读取。如果需要离线处理数据,可以将其加载到 DataSet
中。数据访问层应使用工厂模式或依赖注入来管理连接和命令对象。
2、如果在生产环境中,数据库访问速度变慢,你会如何排查和解决这个问题?
- 检查 SQL 查询是否存在效率问题(如缺少索引)。
- 监控数据库服务器的性能指标,如 CPU 和内存使用率。
- 检查应用程序中是否存在连接泄漏或长时间未关闭的连接。
- 使用数据库的执行计划来分析查询的性能瓶颈。