在 Entity Framework Core (EF Core) 中,虽然提供了高级的 LINQ 查询和对象映射功能,但在某些特定场景下(如执行复杂 SQL、优化性能或调用存储过程),你可能需要直接使用 ADO.NET。EF Core 提供了多种方式与底层的 ADO.NET 进行交互,下面从几种常见场景详细介绍:
1. 通过 DbContext 获取 ADO.NET 连接
DbContext 提供了对底层数据库连接的访问,你可以直接使用这个连接执行 ADO.NET 命令。
1.1 获取数据库连接对象
using (var context = new ApplicationDbContext())
{
// 获取 DbConnection 对象(已由 EF Core 管理)
var connection = context.Database.GetDbConnection();
try
{
// 确保连接已打开(EF Core 可能已自动打开)
if (connection.State != System.Data.ConnectionState.Open)
{
await connection.OpenAsync();
}
// 创建 ADO.NET 命令
using (var command = connection.CreateCommand())
{
command.CommandText = "SELECT COUNT(*) FROM Users WHERE Age > @Age";
command.Parameters.AddWithValue("@Age", 18);
// 执行命令
var count = (int)await command.ExecuteScalarAsync();
Console.WriteLine($"年龄大于 18 的用户数量: {count}");
}
}
finally
{
// 无需手动关闭连接,EF Core 会管理连接生命周期
// 若连接是由 EF Core 打开的,它会在适当的时候关闭
}
}
1.2 使用事务集成
如果需要与 EF Core 的事务集成,可以使用 DbContext.Database.BeginTransaction():
using (var context = new ApplicationDbContext())
using (var transaction = await context.Database.BeginTransactionAsync())
{
// 获取已在事务中的连接
var connection = context.Database.GetDbConnection();
using (var command = connection.CreateCommand())
{
command.Transaction = transaction.GetDbTransaction();
command.CommandText = "UPDATE Users SET LastLogin = GETDATE() WHERE Id = @Id";
command.Parameters.AddWithValue("@Id", 1);
await command.ExecuteNonQueryAsync();
}
// EF Core 操作(与 ADO.NET 操作在同一事务中)
var user = await context.Users.FindAsync(1);
user.Status = "Active";
await context.SaveChangesAsync();
await transaction.CommitAsync();
}
2. 使用 FromSqlRaw/ExecuteSqlRaw 方法
EF Core 提供了 FromSqlRaw 和 ExecuteSqlRaw 方法,允许执行原始 SQL 语句,这些方法内部使用 ADO.NET 实现。
2.1 查询数据
var users = await context.Users
.FromSqlRaw("SELECT * FROM Users WHERE Age > {0}", 18)
.ToListAsync();
2.2 执行非查询命令
var rowsAffected = await context.Database.ExecuteSqlRawAsync(
"UPDATE Users SET LastLogin = GETDATE() WHERE Id = {0}", 1);
2.3 调用存储过程
// 定义存储过程参数
var userId = new SqlParameter("@UserId", 1);
var outputParam = new SqlParameter("@Result", SqlDbType.Int) { Direction = ParameterDirection.Output };
// 执行存储过程
await context.Database.ExecuteSqlRawAsync(
"EXEC sp_UserLogin @UserId, @Result OUTPUT", userId, outputParam);
// 获取输出参数值
var result = (int)outputParam.Value;
3. 使用 RelationalCommandBuilder 构建命令
对于更复杂的场景,可以使用 RelationalCommandBuilder 构建 ADO.NET 命令:
using (var context = new ApplicationDbContext())
{
var commandBuilder = context.GetService<IRelationalCommandBuilderFactory>()
.Create();
commandBuilder
.Append("SELECT * FROM Users WHERE Age > ")
.AppendParameter("age", 18);
var command = commandBuilder.Build();
using (var connection = context.Database.GetDbConnection())
{
await connection.OpenAsync();
using (var dbCommand = command.CreateDbCommand())
{
dbCommand.Connection = connection;
using (var reader = await dbCommand.ExecuteReaderAsync())
{
while (await reader.ReadAsync())
{
var id = reader.GetInt32(0);
var name = reader.GetString(1);
Console.WriteLine($"User: {id}, {name}");
}
}
}
}
}
4. 性能考虑
直接使用 ADO.NET 通常用于性能优化,但需要注意:
4.1 避免 N+1 查询问题
对于复杂查询,直接使用 ADO.NET 可以避免 EF Core 的 N+1 查询问题:
// EF Core 可能产生 N+1 查询
var orders = await context.Orders
.Include(o => o.Customer)
.ToListAsync();
// 使用 ADO.NET 执行单个查询
var sql = @"
SELECT o.Id, o.OrderDate, c.Id, c.Name
FROM Orders o
JOIN Customers c ON o.CustomerId = c.Id";
using (var command = connection.CreateCommand())
{
command.CommandText = sql;
// 处理结果集...
}
4.2 批量操作
对于大批量数据操作,ADO.NET 的 SqlBulkCopy 比 EF Core 更高效:
using (var context = new ApplicationDbContext())
{
var connection = (SqlConnection)context.Database.GetDbConnection();
await connection.OpenAsync();
using (var bulkCopy = new SqlBulkCopy(connection))
{
bulkCopy.DestinationTableName = "Users";
await bulkCopy.WriteToServerAsync(dataTable); // dataTable 包含要插入的数据
}
}
5. 注意事项
- 连接管理:EF Core 管理连接生命周期,通常不需要手动打开 / 关闭连接。
- 参数化查询:始终使用参数化查询(如
{0}或@Parameter)避免 SQL 注入。 - 事务集成:确保 ADO.NET 命令与 EF Core 操作在同一事务中(如果需要原子性)。
- 映射复杂性:手动处理结果集映射比 EF Core 的自动映射更复杂。
- 跨数据库兼容性:直接编写的 SQL 可能不支持多种数据库提供者。
总结
在 EF Core 中使用 ADO.NET 主要通过获取底层数据库连接或使用原始 SQL 方法实现。这种方式适合执行复杂 SQL、优化性能或调用存储过程,但需要权衡手动管理的复杂性和潜在的兼容性问题。在大多数情况下,优先使用 EF Core 的高级 API,仅在必要时才使用 ADO.NET 进行底层操作。
Entity Framework Core 中使用 ADO.NET
1155

被折叠的 条评论
为什么被折叠?



