2

I'm trying to generate DataTable object to pass as a value (TVP) to a stored procured input.

The problem is that the TVP has two auto generated columns, one is an identity and the other is computed.

Calling ExecuteNonQuery fails with an exception:

  • "INSERT into an identity column not allowed on table variables." for the identity column

  • "The column "XXX" cannot be modified because it is either a computed column or is the result of a UNION operator." for the computed column.

  • "Trying to pass a table-valued parameter with 2 column(s) where the corresponding user-defined table type requires 3 column(s)." in case I'm trying to pass a DataTable without one of the coulmns

Here is a code example with the issue-

SQL:

USE tempdb;
GO  
     
IF TYPE_ID('dbo.UDT_stackoverflow') IS NOT NULL
BEGIN
    SET NOEXEC ON;
END;
GO
CREATE TYPE dbo.UDT_StackOverflow AS TABLE
     (
         [ID]       INT        NOT NULL IDENTITY(1, 1),
         PDay       AS CONVERT(DATETIME, CONVERT(DATE, IDateTime))PERSISTED,
         ServerID  INT        NOT NULL,
         IDateTime DATETIME NOT NULL
     );
GO
SET NOEXEC OFF;
GO
    
IF OBJECT_ID('dbo.USP_Bulk_SetStackOverflow') IS NULL
BEGIN
    EXEC('CREATE PROCEDURE dbo.USP_Bulk_SetStackOverflow AS');
END;
GO
ALTER PROCEDURE dbo.USP_Bulk_SetStackOverflow @I_StackOverflow dbo.UDT_StackOverflow READONLY
AS
------------------------------------------------------------------------------------------------------
BEGIN
    SET NOCOUNT ON;
------------------------------------- Main Execution Code -------------------------------------------
    SELECT ID , PDay, ServerID, IDateTime FROM @I_StackOverflow ;
END;
GO

C#:

SqlDatabase db = new SqlDatabase(ConfigurationManager.ConnectionStrings["tempdb"].ConnectionString);
DbCommand dbCommand = db.GetStoredProcCommand("dbo.USP_Bulk_SetStackOverflow");
                
SqlParameter p = (SqlParameter)db.DbProviderFactory.CreateParameter();
p.SqlDbType = SqlDbType.Structured;
p.ParameterName = "@I_StackOverflow";
p.TypeName = "dbo.UDT_StackOverflow";

DataTable table = new DataTable();

table.Columns.Add(new DataColumn() { ColumnName = "ID", Unique = true, AutoIncrement = true, AllowDBNull = false, ReadOnly = true, DataType = typeof(int) });
table.Columns.Add(new DataColumn() { ColumnName = "PDay", DataType = typeof(DateTime) });
table.Columns.Add(new DataColumn() { ColumnName = "ServerID", DataType = typeof(int) });
table.Columns.Add(new DataColumn() { ColumnName = "IDateTime", DataType = typeof(DateTime) });
                
DataRow row = table.NewRow();
row["ServerID"] = 1;
row["IDateTime"] = DateTime.Now;
table.Rows.Add(row);

DataRow row2 = table.NewRow();
row2["ServerID"] = 2;
row2["IDateTime"] = DateTime.Now;
table.Rows.Add(row2);

p.Value = table;

dbCommand.Parameters.Add(p);

db.ExecuteNonQuery(dbCommand);
Netanel Swartz
  • 137
  • 1
  • 5
  • TVPs are read only. What is your code? – Crowcoder Mar 15 '21 at 12:42
  • No one can debug code that cannot be seen. – SMor Mar 15 '21 at 13:51
  • 2
    Does this answer your question? [Identity column in a table-valued parameter in procedure, how to define DataTable](https://stackoverflow.com/questions/3210945/identity-column-in-a-table-valued-parameter-in-procedure-how-to-define-datatabl) You need to pass it through as `IEnumerable`, annoying but that's how it is – Charlieface Mar 15 '21 at 14:31
  • 1
    This solves the Identity issue only but in case of computed columns it's still not working. I would also be glad to keep on using DataTable and not IEnumerable – Netanel Swartz Mar 15 '21 at 14:45
  • If I am not mistaken, you can just not pass through the computed column, don't have a chance to test at the moment – Charlieface Mar 15 '21 at 21:22

0 Answers0