C#读取SQL Server存储过程多结果集拆分至列表与ViewBag方法
解决方案
针对当前场景的直接实现
你原有代码的问题是误用了Read()方法——这个方法是用来遍历单个结果集内的行的,读取多个结果集需要调用SqlDataReader的NextResult()方法逐一切换结果集,不需要逐行手动写映射,直接用DataTable.Load()就能把当前结果集的全部数据灌入对应DataTable。
修正后的代码片段:
public void financeData(int id, string purchase_number) { // 数据库连接务必用using包裹,自动释放资源,避免连接泄漏 using (SqlConnection con = new SqlConnection(constr)) { string query = "getData"; using (SqlCommand command = new SqlCommand(query, con)) { command.CommandType = CommandType.StoredProcedure; // 建议显式指定参数类型,AddWithValue在部分场景下会因为类型推断错误导致索引失效 command.Parameters.Add("@id", SqlDbType.Int).Value = id; command.Parameters.Add("@purchase_number", SqlDbType.NVarChar, 50).Value = purchase_number; DataTable top_purchases = new DataTable(); DataTable transaction_summary = new DataTable(); DataTable maximum_amount = new DataTable(); con.Open(); using (SqlDataReader reader = command.ExecuteReader()) { // 加载第一个结果集 top_purchases.Load(reader); // 切换到第二个结果集 reader.NextResult(); transaction_summary.Load(reader); // 切换到第三个结果集 reader.NextResult(); maximum_amount.Load(reader); } } } // 后续直接把三个DataTable赋值给ViewBag/ViewData即可 // ViewBag.TopPurchases = top_purchases; // ViewBag.TransactionSummary = transaction_summary; // ViewBag.MaximumAmount = maximum_amount; }
注意:结果集的读取顺序必须和存储过程里SELECT语句的编写顺序完全一致,否则会出现数据错位。
通用封装方案(适配所有多结果集存储过程)
如果后续要新增大量同类存储过程,完全可以把多结果集读取的逻辑封装成通用方法,不用每次重复写ADO.NET的模板代码。下面是可直接复用的封装:
第一步:封装通用多结果集读取方法
/// <summary> /// 执行存储过程,返回多个结果集 /// </summary> /// <param name="connectionString">数据库连接字符串</param> /// <param name="storedProcedureName">存储过程名</param> /// <param name="parameters">存储过程参数,键为参数名,值为参数值</param> /// <returns>按存储过程SELECT顺序返回的DataTable列表</returns> public static List<DataTable> ExecuteStoredProcedureWithMultipleResults(string connectionString, string storedProcedureName, Dictionary<string, object> parameters = null) { List<DataTable> resultTables = new List<DataTable>(); using (SqlConnection con = new SqlConnection(connectionString)) { using (SqlCommand cmd = new SqlCommand(storedProcedureName, con)) { cmd.CommandType = CommandType.StoredProcedure; // 自动追加参数 if (parameters != null) { foreach (var param in parameters) { cmd.Parameters.AddWithValue(param.Key, param.Value ?? DBNull.Value); } } con.Open(); using (SqlDataReader reader = cmd.ExecuteReader()) { // 循环读取所有结果集,直到没有下一个结果为止 do { DataTable dt = new DataTable(); dt.Load(reader); resultTables.Add(dt); } while (reader.NextResult()); } } } return resultTables; }
第二步:业务代码直接调用
原来的财务数据读取逻辑可以简化成几行代码:
public void financeData(int id, string purchase_number) { var parameters = new Dictionary<string, object> { {"@id", id}, {"@purchase_number", purchase_number} }; List<DataTable> results = ExecuteStoredProcedureWithMultipleResults(constr, "getData", parameters); // 按索引取对应结果集即可,索引从0开始,对应存储过程里第1个SELECT的结果 DataTable top_purchases = results[0]; DataTable transaction_summary = results[1]; DataTable maximum_amount = results[2]; // 后续赋值ViewBag逻辑不变 }
可选扩展
如果不想用弱类型的DataTable,还可以在通用方法基础上追加扩展,支持把结果集直接映射到指定的强类型List<T>,适配需要强类型处理的场景。
内容的提问来源于stack exchange,提问作者Klaus
相关产品推荐
相关产品推荐

