如何用Dapper QueryMultiple将多结果集映射为强类型对象?
优化Dapper调用多结果集存储过程的方案
先解决你遇到的基础疑问
- 为什么
grid.Read()看不到完整结果,grid.ReadSingle()可以?Read()返回的是结果集的行枚举,第一个结果集只有1行,所以Read()返回的是包含单个动态对象的枚举,直接Dump会显示枚举容器而非内部对象;ReadSingle()则直接取出这唯一的一行对象,所以能看到具体数据。 - 为什么
grid.ReadSingle()["Status"]报错?
你的存储过程第一个SELECT没有指定列名,SQL Server会默认给列命名为Column1和Column2,而非Status和Msg,所以用["Status"]找不到对应键值才会报错。
最优实现方案(分两种场景)
场景1:允许修改存储过程(推荐)
如果能修改存储过程,去掉第二个结果集的FOR XML RAW,直接返回表数据,同时给第一个结果集指定列名,这样Dapper可以直接映射到强类型,完全避免类型转换和XML解析:
修改后的存储过程
CREATE PROCEDURE [dbo].[dapper_test_sproc] AS BEGIN SET NOCOUNT ON ; DECLARE @Status INT = 1 DECLARE @Msg VARCHAR(256) = 'Hello World' DECLARE @StatusLines TABLE ( rule_code VARCHAR(20), rule_name VARCHAR(30), [status] INT, rule_error_msg VARCHAR(256) ) INSERT INTO @StatusLines VALUES ('TEST1', 'TEST2', 1, 'TEST3') -- 给结果列指定明确名称 SELECT @Status AS Status, @Msg AS Message -- 直接返回表数据,不用XML SELECT rule_code, rule_name, [status], rule_error_msg FROM @StatusLines END
对应的C#代码
public async Task dapper_test_sproc() { using var connection = this.Connection; var grid = await connection.QueryMultipleAsync( new CommandDefinition( "dapper_test_sproc", commandType: CommandType.StoredProcedure, cancellationToken: this.QueryCancelToken)); // 直接映射到强类型,无需类型转换 var header = await grid.ReadSingleAsync<ProcedureResultHeader>(); // 直接映射到实体列表 var statusLines = await grid.ReadAsync<StatusLines>().ToListAsync(); var validationMessage = new PasswordValidationStatusMessage { Error = !Convert.ToBoolean(header.Status), Message = header.Message }; validationMessage.Dump(); statusLines.Dump(); } // 对应第一个结果集的强类型 public class ProcedureResultHeader { public int Status { get; set; } public string Message { get; set; } } // 对应第二个结果集的实体类 public class StatusLines { public string RuleCode { get; set; } public string RuleName { get; set; } public bool Status { get; set; } public string RuleErrorMsg { get; set; } } public class PasswordValidationStatusMessage { public bool Error { get; set; } public string Message { get; set; } }
场景2:无法修改存储过程
如果不能改存储过程,那至少给第一个结果集的列名通过Dapper特性映射,同时优化XML解析代码:
优化后的C#代码
public async Task dapper_test_sproc() { using var connection = this.Connection; var grid = await connection.QueryMultipleAsync( new CommandDefinition( "dapper_test_sproc", commandType: CommandType.StoredProcedure, cancellationToken: this.QueryCancelToken)); // 用强类型接收第一个结果集,通过ColumnAttribute映射默认列名 var header = await grid.ReadSingleAsync<ProcedureResultHeader>(); // 读取第二个结果集的XML字符串 var xmlResult = await grid.ReadSingleAsync<XmlResult>(); var statusLinesXML = xmlResult.XmlContent; var validationMessage = new PasswordValidationStatusMessage { Error = !Convert.ToBoolean(header.Status), Message = header.Message }; // 优化XML解析代码,复用序列化器(避免重复创建) var serializer = new XmlSerializer(typeof(StatusLinesEnvelope)); using var reader = new StringReader($"<root>{statusLinesXML}</root>"); var statusLines = (StatusLinesEnvelope)serializer.Deserialize(reader); validationMessage.Dump(); statusLines.Dump(); } // 映射第一个结果集的默认列名(Column1、Column2) public class ProcedureResultHeader { [Column("Column1")] public int Status { get; set; } [Column("Column2")] public string Message { get; set; } } // 映射第二个结果集的XML列(默认列名是Column1) public class XmlResult { [Column("Column1")] public string XmlContent { get; set; } } // 原有的XML实体类保持不变 [XmlRoot(ElementName = "row", Namespace = "")] public class StatusLines { [XmlAttribute(AttributeName = "rule_code")] public string RuleCode { get; set; } [XmlAttribute(AttributeName = "rule_name")] public string RuleName { get; set; } [XmlAttribute(AttributeName = "status")] public bool Status { get; set; } [XmlAttribute(AttributeName = "rule_error_msg")] public string RuleErrorMsg { get; set; } } [XmlRoot(ElementName = "root")] public class StatusLinesEnvelope { [XmlElement(ElementName = "row")] public List<StatusLines> Row { get; set; } } public class PasswordValidationStatusMessage { public bool Error { get; set; } public string Message { get; set; } }
核心优化点
- 使用强类型接收结果集:避免动态类型的强制转换和索引访问,减少出错概率,代码更易读维护。
- 明确列名映射:无论是修改存储过程指定列名,还是用
[Column]特性映射默认列名,都能让Dapper正确匹配属性。 - 避免重复创建序列化器:如果需要解析XML,尽量复用
XmlSerializer实例(可静态初始化),提升性能。
内容的提问来源于stack exchange,提问作者Victorio Berra
相关产品推荐
相关产品推荐

