如何通过SqlDataSource向存储过程传递用户自定义类型参数
问题分析与解决方案
一、SqlDataSource传递表值参数失败的核心原因(你遗漏的配置)
SqlDataSource的内置参数控件(如SessionParameter)不原生支持SQL Server的内存优化表值参数(TVP),具体问题点:
- 缺少表类型映射配置:SessionParameter无法自动识别Session中DataTable与数据库内存优化表类型的对应关系,必须手动指定参数的类型名称和结构。
- 内存优化表的特殊限制:内存优化用户自定义类型需要特定的参数绑定逻辑,默认参数处理流程不支持直接转换。
- 未介入参数绑定流程:SqlDataSource不会自动完成DataTable到内存优化表参数的转换,必须通过事件或手动代码干预。
二、替代方案
方案1:通过SqlDataSource的Selecting事件手动绑定参数
保留SqlDataSource的基础配置,在后台代码中手动处理表值参数的绑定:
protected void sdsOrders_Selecting(object sender, SqlDataSourceSelectingEventArgs e) { // 从Session获取数据表 DataTable contactGroupDt = Session["ContactGroupIds"] as DataTable; DataTable productIdsDt = Session["ProductIds"] as DataTable; // 构造内存优化表值参数 SqlParameter contactGroupParam = new SqlParameter("@P_ContactGroupIds", SqlDbType.Structured) { Value = contactGroupDt, TypeName = "dbo.Integer_List_tblType_InMemory" // 必须指定数据库中完整的类型名 }; SqlParameter productIdsParam = new SqlParameter("@P_ProductIds", SqlDbType.Structured) { Value = productIdsDt, TypeName = "dbo.Integer_List_tblType_InMemory" }; // 添加参数到命令集合,同时移除原配置的SessionParameter避免冲突 e.Command.Parameters.Remove("@P_ContactGroupIds"); e.Command.Parameters.Remove("@P_ProductIds"); e.Command.Parameters.Add(contactGroupParam); e.Command.Parameters.Add(productIdsParam); }
修改前端SqlDataSource配置,移除原SessionParameter并绑定事件:
<asp:SqlDataSource runat="server" ID="sdsOrders" SelectCommand="GET_ListOfOrders" SelectCommandType="StoredProcedure" ProviderName="System.Data.SqlClient" OnSelecting="sdsOrders_Selecting"> <SelectParameters> <asp:QueryStringParameter Name="P_ContactId" QueryStringField="ContractId" DbType="Int64" /> <asp:ControlParameter ControlID="txtDocumentSearch" PropertyName="Text" Name="P_SearchText" /> </SelectParameters> </asp:SqlDataSource>
方案2:后台直接调用存储过程
完全绕过SqlDataSource,使用SqlConnection+SqlCommand手动执行存储过程,灵活性更高:
public DataTable LoadOrders() { DataTable result = new DataTable(); string connStr = ConfigurationManager.ConnectionStrings["YourConnString"].ConnectionString; using (SqlConnection conn = new SqlConnection(connStr)) { using (SqlCommand cmd = new SqlCommand("GET_ListOfOrders", conn)) { cmd.CommandType = CommandType.StoredProcedure; // 添加普通参数 cmd.Parameters.Add("@P_ContactId", SqlDbType.BigInt).Value = long.Parse(Request.QueryString["ContractId"]); cmd.Parameters.Add("@P_SearchText", SqlDbType.NVarChar, 150).Value = txtDocumentSearch.Text; cmd.Parameters.Add("@P_CurrentPageIndex", SqlDbType.BigInt).Value = DBNull.Value; // 根据实际逻辑赋值 cmd.Parameters.Add("@P_PageSize", SqlDbType.BigInt).Value = DBNull.Value; // 添加表值参数 DataTable contactGroupDt = Session["ContactGroupIds"] as DataTable; cmd.Parameters.Add(new SqlParameter("@P_ContactGroupIds", SqlDbType.Structured) { Value = contactGroupDt, TypeName = "dbo.Integer_List_tblType_InMemory" }); DataTable productIdsDt = Session["ProductIds"] as DataTable; cmd.Parameters.Add(new SqlParameter("@P_ProductIds", SqlDbType.Structured) { Value = productIdsDt, TypeName = "dbo.Integer_List_tblType_InMemory" }); conn.Open(); using (SqlDataAdapter da = new SqlDataAdapter(cmd)) { da.Fill(result); } } } // 绑定到目标控件(如GridView) GridView1.DataSource = result; GridView1.DataBind(); return result; }
方案3:改用Entity Framework调用存储过程
如果项目允许,使用EF可以更简洁地处理表值参数:
- 在EF模型中导入自定义表类型和存储过程。
- 创建与表类型匹配的实体类(如
IntegerListModel,包含RecordId属性)。 - 将Session中的DataTable转换为实体集合,作为参数传入EF的存储过程调用。
关键注意事项
- 传入的DataTable结构必须与数据库中内存优化表类型完全一致(列名、数据类型、约束匹配)。
- 表值参数必须指定
TypeName为数据库中自定义类型的完整名称(包含架构名,如dbo.Integer_List_tblType_InMemory)。
内容的提问来源于stack exchange,提问作者Kasim Husaini
相关产品推荐
相关产品推荐

