推荐使用Dapper或EF Core执行原生SQL:Dapper适用于高性能、复杂查询场景,通过Query和参数化防注入;EF Core使用FromSqlInterpolated支持安全内插查询,仅限于查询操作,写操作需用ExecuteSqlInterpolatedAsync,并始终避免SQL拼接以确保安全。
在 C# 中执行原生 SQL 查询,最常用且推荐的方式是通过 Dapper 或 EF Core 的 FromSqlRaw(注意:EF Core 7+ 已弃用 FromSql,统一用 FromSqlRaw 或 FromSqlInterpolated)。两者适用场景不同:Dapper 轻量直接,适合复杂查询或性能敏感场景;EF Core 更适合已建模、需结合实体生命周期的场景。
Dapper 是一个微型 ORM,基于 ADO.NET 封装,支持强类型映射和参数化查询,语法简洁高效。
Dapper
IDbConnection(如 SqlConnection),并保持打开状态Query(返回多行)、QuerySingle、Execute(非查询)等方法示例:
using (var conn = new SqlConnection(connectionString))
{
conn.Open();
var users = conn.Query(
"SELECT * FROM Users WHERE Age > @minAge AND Status = @status",
new { minAge = 18, status = "Active" });
} EF Core 允许对 DbSet 直接执行原生 SQL,但有严格限制:只能用于查询(不能用于 INSERT/UPDATE/DELETE),且返回类型必须与实体或投影类型匹配。
FromSqlRaw:接受格式化字符串,需手动处理参数占位符(如 {0} 不安全,应配合 SqlParameter)FromSqlInterpolated:支持内插字符串($""),自动参数化,更安全、更推荐EnableSensitiveDataLogging 并配置允许)AsEnumerable() 后续处理示例:
var minAge = 18; var status = "Active";
var users = context.Users .FromSqlInterpolated($"SELECT * FROM Users WHERE Age > {minAge} AND Status = {status}") .ToList();
无论是 Dapper 还是 EF Core,都不通过 FromSql 做写操作。
Execute 方法,返回影响行数Database.ExecuteSqlRaw 或 Database.ExecuteSqlInterpolated
ExecuteAsync / ExecuteSqlRawAsync)示例(EF Core):
var rows = await context.Database.ExecuteSqlInterpolatedAsync(
$"UPDATE Users SET Status = {newStatus} WHERE Id = {userId}");原生 SQL 灵活但风险高,关键点要牢记:
FromSql* 只能作用于根 DbSet,不能链式调用 Where 等 LINQ 方法后再用 FromSql
基本上就这些。选 Dapper 还是 EF Core,取决于你是否需要实体跟踪、变更检测、迁移等高级功能——轻量查用 Dapper,业务模型重、需统一 ORM 体验就用 EF Core 的原生 SQL 支持。