.NET Core 6中Entity Framework Core调用表值函数(TVF)的问题
.NET Core 6 API调用带参表值函数(TVF)的解决方案
推荐方案:使用EF Core的FromSqlRaw/FromSqlInterpolated
.NET Core 6中EF Core已移除SqlQuery方法,官方推荐用FromSqlRaw(处理原生SQL字符串)或FromSqlInterpolated(安全传递参数、避免SQL注入)调用表值函数,无需手动管理数据库连接,能直接规避「连接当前状态为关闭」这类问题。
步骤1:定义匹配TVF返回结果的实体类
创建一个和TVF输出字段完全对应的实体类,假设你的TVF返回Id、ProductName、Price三个字段:
public class TvfProductResult { public int Id { get; set; } public string ProductName { get; set; } public decimal Price { get; set; } }
步骤2:在DbContext中注册无键实体
在你的DbContext中添加DbSet<TvfProductResult>,并配置为无键实体(因为TVF不是物理数据表):
public DbSet<TvfProductResult> TvfProductResults { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.Entity<TvfProductResult>().HasNoKey(); }
步骤3:在API控制器中实现GET调用
在GET方法中通过EF Core执行TVF调用,传入参数并返回JSON结果:
[ApiController] [Route("api/[controller]")] public class ProductController : ControllerBase { private readonly AppDbContext _dbContext; public ProductController(AppDbContext dbContext) { _dbContext = dbContext; } [HttpGet] public async Task<IActionResult> GetProductsByTvf(int categoryId, string keyword) { // 使用FromSqlInterpolated安全传递参数,自动处理SQL注入风险 var results = await _dbContext.TvfProductResults .FromSqlInterpolated($"SELECT * FROM dbo.GetProductsByCategoryAndKeyword({categoryId}, {keyword})") .ToListAsync(); return Ok(results); } }
手动DbDataReader方案的连接问题修复(可选)
如果坚持使用DbDataReader方式,必须确保连接被正确打开并通过using块管理资源,示例代码如下:
[HttpGet] public async Task<IActionResult> GetProductsByTvf(int categoryId, string keyword) { using var connection = _dbContext.Database.GetDbConnection(); await connection.OpenAsync(); // 必须手动打开数据库连接 using var command = connection.CreateCommand(); command.CommandText = "SELECT * FROM dbo.GetProductsByCategoryAndKeyword(@CategoryId, @Keyword)"; command.Parameters.Add(new SqlParameter("@CategoryId", categoryId)); command.Parameters.Add(new SqlParameter("@Keyword", keyword)); using var reader = await command.ExecuteReaderAsync(); var results = new List<TvfProductResult>(); while (await reader.ReadAsync()) { results.Add(new TvfProductResult { Id = reader.GetInt32(reader.GetOrdinal("Id")), ProductName = reader.GetString(reader.GetOrdinal("ProductName")), Price = reader.GetDecimal(reader.GetOrdinal("Price")) }); } return Ok(results); }
注意:这种方式需要手动处理参数映射、连接生命周期和数据读取逻辑,代码冗余度高,优先推荐使用第一种EF Core原生方案。
内容的提问来源于stack exchange,提问作者Sergio Teijido
相关产品推荐
相关产品推荐

