// See at the end what you need in SQL Server!
using System;
using System.Data;
using System.Data.SqlClient;
using System.Collections.Generic;
using Microsoft.SqlServer.Server;
class TVPdemo {
// Connection string. Change to fit to your environment.
private const string connstr =
"Application Name=TVPdemo;Integrated Security=SSPI;" +
"Data Source=.\\IKI;Initial Catalog=tempdb";
// Procedure to print messages from SQL Server, errors or informational
// messages.
private static void PrintSqlMsgs(SqlErrorCollection msgs) {
foreach (SqlError e in msgs) {
Console.WriteLine (
"Msg {0}, Severity {1}, State: {2}, Procedure {3}, Line no: {4}",
e.Number.ToString(), e.Class.ToString(), e.State.ToString(),
e.Procedure, e.LineNumber.ToString()
);
Console.WriteLine(e.Message);
}
}
// Handler for messages from SQL Server. For this demo, we do not
// distinguish between errors and informational messages.
private static void SqlInfoMessage(object sender,
SqlInfoMessageEventArgs ea) {
PrintSqlMsgs(ea.Errors);
}
// Simple generic routine to print the contents of a data set.
private static void PrintDataSet(DataSet ds) {
Console.WriteLine("============= Dataset =======================");
if (ds.Tables.Count == 0) {
Console.WriteLine("Empty dataset");
}
else {
foreach (DataTable tbl in ds.Tables) {
Console.WriteLine("----------------------------------------");
foreach (DataColumn col in tbl.Columns) {
Console.Write(col.ColumnName + "\t");
}
Console.WriteLine();
foreach (DataRow row in tbl.Rows) {
foreach (DataColumn col in tbl.Columns) {
Console.Write(row[col].ToString() + "\t");
}
Console.WriteLine();
}
}
}
}
// This helper routine sets up our SQL Connection.
private static SqlConnection setup_connection () {
// Open the connection.
SqlConnection cn = new SqlConnection(connstr);
// Handle user errors with callbacks, rather than exception.
cn.InfoMessage += SqlInfoMessage;
cn.FireInfoMessageEventOnUserErrors = true;
return cn;
}
// This procedure shows how pass a value to a TVP to a stored procedure
// using a List<SqlDataRecord>.
private static void list_example() {
// Our sample input data.
int[] products = {9, 12, 27, 37};
// Here we use a List<SqlDataRecord>. SqlDataRecord is the namespace
// using Microsoft.SqlServer.Server.
List <SqlDataRecord> product_list = new List<SqlDataRecord>();
// Create an SqlMetaData object that describes our table type.
SqlMetaData[] tvp_definition = {new SqlMetaData("n", SqlDbType.Int)};
// Loop over the products.
foreach (int prodid in products) {
// Create a new record, using the metadata array above.
SqlDataRecord rec = new SqlDataRecord(tvp_definition);
rec.SetInt32(0, prodid); // Set the value.
product_list.Add(rec); // Add it to the list.
}
// Open a connection.
using (SqlConnection cn = setup_connection()) {
// Set up an SqlCommand to call the stored procedure.
using (SqlCommand cmd = cn.CreateCommand()) {
// The procedure call itself.
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "dbo.get_product_names";
// Add the table parameter, for table enum value to use is
// Structured. TVPs are input only.
cmd.Parameters.Add("@prodids", SqlDbType.Structured);
cmd.Parameters["@prodids"].Direction = ParameterDirection.Input;
// We should specify the table type (although when calling a
// a procedure, this is not mandatory).
cmd.Parameters["@prodids"].TypeName = "integer_list_tbltype";
// Then we specify the value, this is our List<SqlDataRecord>.
cmd.Parameters["@prodids"].Value = product_list;
// Time to run the command. To keep the code brief we use a
// DataAdapter.Fill, although this may not be the most efficient.
using (SqlDataAdapter da = new SqlDataAdapter(cmd))
using (DataSet ds = new DataSet()) {
da.Fill(ds);
PrintDataSet(ds);
}
}
}
}
// This procedure shows how to pass a TVP using a DataTable. This time
// we don't use a stored procedure, but a plain SQL statement to show
// that this is possible.
private static void datatable_example() {
// Our sample input data.
string [] custids = {"ALFKI", "BONAP", "CACTU", "FRANK"};
// Create a DataTable, and define its single column.
DataTable custid_list = new DataTable();
custid_list.Columns.Add("custid", typeof(String));
// Loop over the customer in the table.
foreach (string custid in custids) {
// Create a new row, save the customer id, and add it to the table.
DataRow dr = custid_list.NewRow();
dr["custid"] = custid;
custid_list.Rows.Add(dr);
}
// Open connection.
using(SqlConnection cn = setup_connection()) {
// Set up the command.
using(SqlCommand cmd = cn.CreateCommand()) {
// The query to run.
cmd.CommandText =
@"SELECT C.CustomerID, C.CompanyName
FROM Northwind.dbo.Customers C
WHERE C.CustomerID IN (SELECT id.custid FROM @custids id)";
cmd.CommandType = CommandType.Text;
// Add the table parameter.
cmd.Parameters.Add("@custids", SqlDbType.Structured);
cmd.Parameters["@custids"].Direction = ParameterDirection.Input;
// When we use CommandType.Text, we must specify the name of
// the table type.
cmd.Parameters["@custids"].TypeName = "custid_list_tbltype";
// We pass our data table as the parameter value.
cmd.Parameters["@custids"].Value = custid_list;
// Time to run the command. To keep the code brief we use a
// DataAdapter.Fill, although this may not be the most efficient.
using (SqlDataAdapter da = new SqlDataAdapter(cmd))
using (DataSet ds = new DataSet()) {
da.Fill(ds);
PrintDataSet(ds);
}
}
}
}
// This example shows how to pass data using a DataReader. The example
// is a little cheesy, as it just reads a list of values from an SQL
// command. Normally, you would use a DataReader when you have your
// input in some other data source: a file, an access database, or even
// a TCP port. (In the latter case you would need to implement
// IDataReader yourself.)
private static void datareader_example() {
// Set up a connection using OleDb client; we could at least
// pretend that it is something else than SQL Server.
System.Data.OleDb.OleDbConnection remote_conn =
new System.Data.OleDb.OleDbConnection(
"Provider=SQLOLEDB;" + connstr);
remote_conn.Open();
// Set up the command.
System.Data.OleDb.OleDbCommand remote_cmd =
remote_conn.CreateCommand();
remote_cmd.CommandType = CommandType.Text;
remote_cmd.CommandText =
"SELECT n FROM (VALUES(9), (12), (27), (37)) as t (n)";
// Set up the reader.
System.Data.OleDb.OleDbDataReader oledb_reader =
remote_cmd.ExecuteReader();
// Now we have the reader, we can run the SQL Server command as
// in the other two examples.
using (SqlConnection cn = setup_connection()) {
using (SqlCommand cmd = cn.CreateCommand()) {
// The procedure call.
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "dbo.get_product_names";
// Add the table parameter.
cmd.Parameters.Add("@prodids", SqlDbType.Structured);
cmd.Parameters["@prodids"].Direction = ParameterDirection.Input;
cmd.Parameters["@prodids"].TypeName = "integer_list_tbltype";
// Then we specify the value, and this time this is the
// OleDB data reader.
cmd.Parameters["@prodids"].Value = oledb_reader;
// And run the command.
using (SqlDataAdapter da = new SqlDataAdapter(cmd))
using (DataSet ds = new DataSet()) {
da.Fill(ds);
PrintDataSet(ds);
}
}
}
// Cleanup the remote reader.
oledb_reader.Close();
oledb_reader.Dispose();
remote_cmd.Dispose();
remote_conn.Dispose();
}
private static void Main() {
// We use a plain exception handler just to make sure that errors are
// printed to command-line window, without getting a message box about
// unhandled exception.
try {
// Run the two examples.
list_example();
datatable_example();
datareader_example();
}
catch (Exception ex) {
// For SQL errors, print the full story. (But note that only SQL
// errors with severity >= 17 will end up here, since we use
// FireInfoMessageEventOnUserErrors.)
if (ex is SqlException) {
SqlException sqlex = (SqlException) ex;
PrintSqlMsgs(sqlex.Errors);
}
else {
Console.WriteLine(ex.ToString());
}
}
}
}
/*
Create this in SQL Server to be able to run the demo code. You will need
the Northwind database.
CREATE TYPE integer_list_tbltype AS TABLE (n int NOT NULL PRIMARY KEY)
CREATE TYPE custid_list_tbltype AS TABLE(custid nchar(5) NOT NULL PRIMARY KEY)
go
CREATE PROCEDURE get_product_names @prodids integer_list_tbltype READONLY AS
SELECT p.ProductID, p.ProductName
FROM Northwind..Products p
WHERE p.ProductID IN (SELECT n FROM @prodids)
*/
http://www.sommarskog.se/arraylist-2008/tvpdemo.cs