在C#项目中使用EF Core进行数据访问时,除了使用LINQ表达式构建查询外,很多时候需要直接执行原始SQL语句来处理复杂的查询逻辑或者执行批量操作。为了避免SQL注入攻击,原始SQL查询必须携带参数,EF Core提供了完善的参数化查询支持。

EF Core原始SQL带参数查询的常用方式
1. 使用FromSqlRaw执行参数化查询返回实体
当需要通过原始SQL查询返回数据库实体对象时,可以使用FromSqlRaw方法,该方法支持传入参数占位符和对应的参数值,EF Core会自动处理参数的转义,避免注入风险。
首先定义对应的实体类和DbContext:
// 用户实体类
public class User
{
public int Id { get; set; }
public string UserName { get; set; }
public int Age { get; set; }
public string Email { get; set; }
}
// 自定义DbContext
public class AppDbContext : DbContext
{
public DbSet<User> Users { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
// 这里使用SQL Server示例,实际可根据数据库类型调整
optionsBuilder.UseSqlServer("Server=localhost;Database=TestDb;Trusted_Connection=True;");
}
}
接下来使用FromSqlRaw执行带参数的查询:
using System;
using System.Linq;
using Microsoft.EntityFrameworkCore;
class Program
{
static void Main()
{
using var dbContext = new AppDbContext();
// 定义查询参数
var minAge = 18;
var targetName = "张三";
// 执行带参数的原始SQL查询,使用@符号作为占位符,参数按顺序传入
var users = dbContext.Users
.FromSqlRaw("SELECT * FROM Users WHERE Age >= @p0 AND UserName = @p1", minAge, targetName)
.ToList();
foreach (var user in users)
{
Console.WriteLine($"用户ID:{user.Id},用户名:{user.UserName},年龄:{user.Age}");
}
}
}
2. 使用ExecuteSqlRaw执行带参数的增删改操作
如果需要执行INSERT、UPDATE、DELETE等不返回实体集合的原始SQL操作,可以使用ExecuteSqlRaw方法,同样支持参数化传入。
using System;
using Microsoft.EntityFrameworkCore;
class Program
{
static void Main()
{
using var dbContext = new AppDbContext();
// 定义更新参数
var newAge = 25;
var userId = 1;
// 执行带参数的更新SQL,返回受影响的行数
var affectedRows = dbContext.Database.ExecuteSqlRaw("UPDATE Users SET Age = @p0 WHERE Id = @p1", newAge, userId);
Console.WriteLine($"更新了{affectedRows}条数据");
}
}
3. 使用DbParameter传递参数
除了按顺序传入参数外,还可以使用DbParameter数组来显式定义参数,这种方式更适合参数较多或者需要明确参数类型的场景。
using System;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.Data.SqlClient; // SQL Server参数类型,其他数据库对应调整
class Program
{
static void Main()
{
using var dbContext = new AppDbContext();
// 定义DbParameter参数
var parameters = new DbParameter[]
{
new SqlParameter("@minAge", 20),
new SqlParameter("@maxAge", 30)
};
// 执行带DbParameter的原始SQL查询
var users = dbContext.Users
.FromSqlRaw("SELECT * FROM Users WHERE Age BETWEEN @minAge AND @maxAge", parameters)
.ToList();
foreach (var user in users)
{
Console.WriteLine($"符合条件的用户:{user.UserName},年龄:{user.Age}");
}
}
}
注意事项
- 使用原始SQL查询时,返回的列必须和实体类的属性一一对应,否则会抛出异常。
- 参数占位符的格式根据数据库类型可能不同,例如SQL Server使用
@p0或者@参数名,MySQL使用?作为占位符,需要根据实际使用的数据库调整。 - 不要直接拼接字符串作为参数值,即使使用了原始SQL,也必须通过参数传入,否则依然会有SQL注入风险。
FromSqlRaw返回的查询可以和其他LINQ操作组合使用,例如追加Where、OrderBy等条件,EF Core会自动将额外的条件合并到最终执行的SQL中。