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