优先用FromSqlInterpolated,它自动参数化插值变量防SQL注入;FromSqlRaw不处理安全,需手动防范。

FromSqlRaw 和 FromSqlInterpolated 哪个更安全?
优先用 FromSqlInterpolated,它自动参数化所有插值变量,底层生成命名参数(如 @p0),不拼字符串,天然防注入。而 FromSqlRaw 完全不处理参数安全——你传什么它就发什么,"SELECT * FROM Users WHERE Name = '" + name + "'" 这种写法在 FromSqlRaw 里等于裸奔。
但注意两个例外:
-
FromSqlInterpolated不处理表达式拼接,比如{name + "%"}或{string.Format("{0}%", name)},C# 层已拼好再插进去,参数化失效 - 模糊查询带通配符时,
FromSqlInterpolated会把%当作字面量,无法直接写{name}%;此时必须退回到FromSqlRaw+SqlParameter,手动构造LIKE @name参数
字段名/表名动态化怎么防注入?
EF Core 的参数化只覆盖值(value),不覆盖结构(identifier):列名、表名、ORDER BY 子句、GROUP BY 字段都不能用 {0} 或 {var} 替代。一旦允许用户输入控制这些部分,就是列名注入高危点。
正确做法只有两个:
- 硬编码:所有字段名、排序方向、表别名都写死在 SQL 字符串里
- 白名单校验:比如用户可选排序字段,只允许
["Id", "Name", "CreatedAt"],用if (!allowedFields.Contains(input)) throw ...拦住,再拼进 SQL;绝不用反射GetProperty(input)或字符串格式化拼接
常见错误:把 orderBy 当成普通参数传给 FromSqlInterpolated,结果生成 ORDER BY @p0 —— 数据库报错或静默忽略,但逻辑已失控。
执行非查询语句时如何避免 ExecuteSqlRaw 破防?
ExecuteSqlRaw 和 ExecuteSqlInterpolated 都不返回实体,只返回受影响行数,但安全水位差一截:ExecuteSqlInterpolated 自动参数化,ExecuteSqlRaw 完全不干预。
实操建议:
- 永远优先写
context.Database.ExecuteSqlInterpolatedAsync($"UPDATE Users SET Status = {status} WHERE Id = {id}") - 若需指定参数类型(如
datetime2或nvarchar(50)),改用ExecuteSqlRaw+SqlParameter数组,例如:new SqlParameter("@status", SqlDbType.NVarChar) { Value = status } - 禁止出现
.ToString()、string.Format、$"..." + userinput等任何形式的字符串拼接
注意:ExecuteSqlInterpolated 不支持异步等待以外的链式调用,也不能在它之后接 .AsNoTracking() —— 那是查询方法才有的扩展。
查询非实体类型时为什么不能用 FromSqlRaw?
FromSqlRaw 只能绑定到 DbSet<T>,且强制要求 SQL 返回字段与实体属性名、类型、顺序严格匹配。一旦你想查 COUNT(*)、AVG(Price) 或自定义视图字段,它就直接抛异常或映射失败。
替代方案明确且固定:
- 标量值(单个数字、字符串):用
context.Database.SqlQueryRaw<int>("SELECT COUNT(*) FROM Orders").Single()(注意类型必须匹配,COUNT在 SQL Server 返回bigint,建议用long) - 自定义 DTO:定义一个轻量类(含 public 构造函数或可空属性),然后
context.Database.SqlQueryRaw<OrderSummary>("SELECT CustomerName, SUM(Amount) AS Total FROM Orders GROUP BY CustomerName") - 完全自由结构:放弃 EF Core 封装,走
context.Database.GetDbConnection()拿原生DbConnection,用 ADO.NET 手动CreateCommand+ExecuteReader
最容易被忽略的一点:所有 SqlQueryRaw 查询默认不跟踪实体,也不受 AsNoTracking 影响——它本来就不进变更跟踪器,这点和 FromSqlRaw 返回的实体行为不同。

















