Passing Table Valued Parameter to Stored Procedure: Part 2

Here you will see how to pass a table valued parameter to a Stored Procedure.

This is Part 2 of passing a table valued parameter to a stored procedure, a 2-part series.
 
In Part 1, I described how to pass a DataTable into a stored procedure using a structured type. So the question is, are there any other types you can pass as a structured parameter? The answer is yes.
 
You can also pass a collection object to a structured type parameter. The collection must implement IEnumerable<SqlDataRecord> in order to be compatible with a structured parameter.
 
In this article I am taking the same example to explain the use of a collection in a structured parameter.
 
1. Create a CustomerDetail table in your database; this will be used to store Customer records.
  1. CREATE TABLE dbo.CustomerDetail(  
  2.       CustomerId INT IDENTITY(1,1) NOT NULL,  
  3.       FirstName VARCHAR(50),  
  4.       LastName VARCHAR(50),  
  5.       Address VARCHAR(100),  
  6.       Email  VARCHAR(100),  
  7.       Phone VARCHAR(10)  
  8. ) 
2. Create a user defined table type which will be used as a table valued parameter for the stored procedure.
  1. CREATE TYPE dbo.CustomerDetailType AS TABLE(  
  2.    FirstName VARCHAR(50),  
  3.    LastName VARCHAR(50),  
  4.    Address VARCHAR(100),  
  5.    Email  VARCHAR(100),  
  6.    Phone VARCHAR(10)  
  7. )   
3. Create the SaveCustomerDetails stored procedure. This stored procedure will accept a table valued parameter. Note that the READONLY keyword is required to declare a table valued parameter; you cannot perform DML operations on a table valued parameter.
  1. CREATE PROCEDURE dbo.SaveCustomerDetail  
  2.    @CustDtl dbo.CustomerDetailType READONLY  
  3. AS  
  4. BEGIN  
  5.    SET NOCOUNT ON  
  6.    INSERT INTO dbo.CustomerDetail (FirstName, LastName, Address, Email, Phone)  
  7.    SELECT FirstName, LastName, Address, Email, Phone FROM @CustDtl  
  8. END  
4. Write C# code to create the collection to be passed into the stored procedure and call the stored procedure.
 
In order to pass a structured type you need to create the collection with the same schema that the user defined table type has. First create a class and add properties; ideally the properties should have the same data type as you have defined in the user defined table type.
  1. public class CustomerDetail  
  2. {  
  3.     public string FirstName { getset; }  
  4.     public string LastName { getset; }  
  5.     public string Address { getset; }  
  6.     public string Email { getset; }  
  7.     public string Phone { getset; }  
  8. }   
Define the collection class; the collection class must be inherited from List<CustomerDetail> and it should also implement
  1. IEnumerable<SqlDataRecord>
  2.   
  3. public class CustomerCollection : List<CustomerDetail>, IEnumerable<SqlDataRecord>  
  4. {  
  5.     IEnumerator<SqlDataRecord> IEnumerable<SqlDataRecord>.GetEnumerator()  
  6.     {  
  7.             var sqlRow = new SqlDataRecord(  
  8.                   new SqlMetaData("FirstName", SqlDbType.VarChar, 50),  
  9.                   new SqlMetaData("LastName", SqlDbType.VarChar, 50),  
  10.                   new SqlMetaData("Address", SqlDbType.VarChar, 100),  
  11.                   new SqlMetaData("Email", SqlDbType.VarChar, 100),  
  12.                   new SqlMetaData("Phone", SqlDbType.VarChar, 10));  
  13.   
  14.             foreach (CustomerDetail cust in this)  
  15.             {  
  16.                   sqlRow.SetString(0, cust.FirstName);  
  17.                   sqlRow.SetString(1, cust.LastName);  
  18.                   sqlRow.SetString(2, cust.Address);  
  19.                   sqlRow.SetString(3, cust.Email);  
  20.                   sqlRow.SetString(4, cust.Phone);  
  21.   
  22.                   yield return sqlRow;  
  23.             }  
  24.     }  
  25. }  
Note that you have defined SqlMetaData the same as the user defined table type, the column name and data type should exactly match.
 
Create a collection object and add test data.
  1. CustomerCollection custColl = new CustomerCollection();  
  2. custColl.Add(new CustomerDetail { FirstName = "AAAA", LastName = "BBBB", Address = "Address1", Email = "aaa@aaa.com", Phone = "99999999999"});  
  3. custColl.Add(new CustomerDetail { FirstName = "BBBB", LastName = "BBBB", Address = "Address1", Email = "bbb@bbb.com", Phone = "99999999999"});  
  4. custColl.Add(new CustomerDetail { FirstName = "CCCC", LastName = "CCCC", Address = "Address1", Email = "ccc@ccc.com", Phone = "99999999999"});  
  5. custColl.Add(new CustomerDetail { FirstName = "DDDD", LastName = "DDDD", Address = "Address1", Email = "ddd@ddd.com", Phone = "99999999999"});  
Now pass the structured type to the stored procedure.
  1. SqlParameter param = new SqlParameter();  
  2. param.ParameterName = "CustDtl";  
  3. param.SqlDbType = SqlDbType.Structured;  
  4. param.Value = custColl;  
  5. param.Direction = ParameterDirection.Input;  
  6.   
  7. String dbConnStr =     ConfigurationManager.ConnectionStrings["DBConStr"].ConnectionString;  
  8. SqlConnection conn = null;  
  9.   
  10. using (conn = new SqlConnection(dbConnStr))  
  11. {  
  12.     SqlCommand sqlCmd = new SqlCommand("dbo.SaveCustomerDetail");  
  13.     conn.Open();  
  14.     sqlCmd.Connection = conn;  
  15.     sqlCmd.CommandType = CommandType.StoredProcedure;  
  16.     sqlCmd.Parameters.Add(param);  
  17.     sqlCmd.ExecuteNonQuery();  
  18. }   
If you think in terms of performance, a collection is a better option for a table valued parameter.