.NET5与EF Core5执行多表返回存储过程遇SqlQuery不存在报错如何解决
解决方案
错误原因
EF Core 5的Database门面原生没有提供SqlQuery<T>泛型方法,该API是EF6的遗留功能,EF Core默认不支持直接将原生SQL查询结果映射到未在DbContext中注册的DTO类型。
实现方案
方案1:注册无键实体(官方推荐)
无需引入额外依赖,符合EF Core的设计规范:
- 首先在你的
MarketContext中注册DetailsClientsDto为无键实体,并添加对应的DbSet:
public class MarketContext : DbContext { // 新增DbSet属性 public DbSet<DetailsClientsDto> DetailsClients { get; set; } protected override void OnModelCreating(ModelBuilder modelBuilder) { // 配置为无键实体,不会映射到实际数据表 modelBuilder.Entity<DetailsClientsDto>().HasNoKey(); } }
- 修改查询代码,使用
FromSqlRaw执行存储过程:
public async Task<DetailsClientsDto> GetDetailsRepository(SearchDetailsDto details) { var sqlParameters = new[] { new SqlParameter { ParameterName = "@ID_USER", Value = details.IdUser, SqlDbType = SqlDbType.Int, }, new SqlParameter { ParameterName = "@ID_CLIENT", // 修正原代码的拼写错误:detalles → details Value = details.IdClient, SqlDbType = SqlDbType.Int, IsNullable = true }, }; // 单结果用FirstOrDefaultAsync,多结果查询替换为ToListAsync即可 return await _context.DetailsClients .FromSqlRaw("EXEC [dbo].[SPROC_DETAILS] @ID_USER, @ID_CLIENT", sqlParameters) .FirstOrDefaultAsync(); }
方案2:原生ADO.NET读取(无需修改DbContext)
如果不想修改DbContext的配置,可以直接从DbContext获取数据库连接,手动映射结果:
using Microsoft.Data.SqlClient; // 注意使用Microsoft.Data.SqlClient而非System.Data.SqlClient public async Task<DetailsClientsDto> GetDetailsRepository(SearchDetailsDto details) { var sqlParameters = new[] { new SqlParameter { ParameterName = "@ID_USER", Value = details.IdUser, SqlDbType = SqlDbType.Int, }, new SqlParameter { ParameterName = "@ID_CLIENT", Value = details.IdClient, SqlDbType = SqlDbType.Int, IsNullable = true }, }; await using var connection = _context.Database.GetDbConnection(); await connection.OpenAsync(); await using var command = connection.CreateCommand(); command.CommandText = "EXEC [dbo].[SPROC_DETAILS] @ID_USER, @ID_CLIENT"; command.Parameters.AddRange(sqlParameters); await using var reader = await command.ExecuteReaderAsync(); if (await reader.ReadAsync()) { return new DetailsClientsDto { IdUser = reader.GetInt32(reader.GetOrdinal(nameof(DetailsClientsDto.IdUser))), IdClient = reader.GetInt32(reader.GetOrdinal(nameof(DetailsClientsDto.IdClient))), User = reader.IsDBNull(reader.GetOrdinal(nameof(DetailsClientsDto.User))) ? null : reader.GetString(reader.GetOrdinal(nameof(DetailsClientsDto.User))), Adress = reader.IsDBNull(reader.GetOrdinal(nameof(DetailsClientsDto.Adress))) ? null : reader.GetString(reader.GetOrdinal(nameof(DetailsClientsDto.Adress))), Car = reader.IsDBNull(reader.GetOrdinal(nameof(DetailsClientsDto.Car))) ? null : reader.GetString(reader.GetOrdinal(nameof(DetailsClientsDto.Car))), Color = reader.IsDBNull(reader.GetOrdinal(nameof(DetailsClientsDto.Color))) ? null : reader.GetString(reader.GetOrdinal(nameof(DetailsClientsDto.Color))) }; } return null; }
注意事项
- 不要用
Task.Run包裹EF Core的异步查询操作,EF Core提供的异步方法本身就是非阻塞的,额外包Task.Run只会浪费线程池资源 - 执行存储过程的SQL语句需要完整列出所有输入参数,原代码中漏传了
@ID_CLIENT参数 - 确保存储过程返回的字段名和
DetailsClientsDto的属性名完全一致,否则会出现映射失败的问题
内容的提问来源于stack exchange,提问作者elun
相关产品推荐
相关产品推荐

