Empty DataTable Causes Errors When Table-Valued Parameter Has VARBINARY Types

空扰寡人 提交于 2019-12-13 08:48:36

问题


I have a C# web application that serves as a passthrough to SQL Server; requests that detail SQL Server commands come in, we parse the request, generate the necessary .Net types and then use them to execute SqlCommands and such. The upshot of that is that the C# web application needs to be very flexible and really can't make too many assumptions about what a request "should" look like.

I recently solved a problem that was causing exceptions to be thrown when a table-valued parameter contained VARBINARY types. In the general case, we loop over the incoming request and construct a DataTable with the appropriate number of columns and rows. The data type of the columns weren't specified, and the data values were just inserted as objects. For VARBINARY types, this would result in the following error:

Implicit conversion from data type nvarchar(max) to varbinary(max) is not allowed. Use the CONVERT function to run this query.

I found this StackOverflow post, and was able to solve the problem. Please see my pseudo-code below:

for (var colNdx = 0; colNdx < requestTvp.Columns.Count; ++colNdx)
{
    myDataTable.Columns.Add();

    if (requestTvp.Rows.Empty)
    {
        continue;
    }

    if (requestTvp.Rows[0].Columns[colNdx].DataType == Bytes)
    {
        myDataTable.Columns[colNdx].DataType = typeof(SqlBinary);
    }
}

From what I can tell, this is working great. The problem occurs when the incoming TVP has no rows since I have no data to check against. In this case, we just construct a DataTable with the correct number of columns (this is specified in the request metadata about the TVP), but these columns have no explicitly set data type. The DataTable has no rows in this case. This results in the same exception:

Implicit conversion from data type nvarchar(max) to varbinary(max) is not allowed. Use the CONVERT function to run this query.

Here is the full stack trace:

System.Data.SqlClient.SqlException (0x80131904): Implicit conversion from data type nvarchar(max) to varbinary(max) is not allowed. Use the CONVERT function to run this query. The data for table-valued parameter "@tvp" doesn't conform to the table type of the parameter.
at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction)
at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose)
at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady)
at System.Data.SqlClient.SqlDataReader.TryConsumeMetaData()
at System.Data.SqlClient.SqlDataReader.get_MetaData()
at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString)
at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Task& task, Boolean asyncWrite, SqlDataReader ds)
at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method, TaskCompletionSource`1 completion, Int32 timeout, Task& task, Boolean asyncWrite)
at System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method)
at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior, String method)
at System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior behavior)

Honestly, I don't understand why the problem is occurring at all...if there's no data in the DataTable what is even causing the invalid implicit conversion?

What makes things even more frustrating, is that this error occurs before the request even gets to my database, so I'm not able to use SQL Server Profiler to figure out exactly what is being sent to the database server.

So here's my question: how do I correctly pass an empty DataTable as table-valued parameter to a stored procedure when the corresponding User-Defined Table Type has a VARBINARY field? The only information I have about the table-valued parameter when I'm constructing the DataTable is the name of the User-Defined Table Type, the number of columns it contains, the name of the parameter in the stored procedure and the values contained in the request TVP, which in this case is essentially an empty set.


回答1:


Here is the work around I have come up with. If the data table is empty, just bypass the SqlCommand objects declaration of the table type and do it yourself:

      using(SqlConnection c = new SqlConnection("<connectionString>"))
        {
            c.Open();

            DataTable emptyTable = new DataTable();

            emptyTable.Columns.Add("c1", typeof(int));
            emptyTable.Columns.Add("c2", typeof(string));

            DataRow row = emptyTable.NewRow();

            row["c1"] = 99;
            row["c2"] = new String('X', Int16.MaxValue);

           // Uncomment to make table non empty
           // emptyTable.Rows.Add(row);

            SqlCommand cmd = c.CreateCommand();

            if (emptyTable.Rows.Count > 0)
            {
                cmd.CommandText = "dbo.BOB";
                cmd.CommandType = System.Data.CommandType.StoredProcedure;
                SqlParameter p = cmd.Parameters.AddWithValue("@TP", emptyTable);

                p.SqlDbType = SqlDbType.Structured;
                p.TypeName = "dbo.KrjVarBin";
            }

            else
            {
                cmd.CommandType = CommandType.Text;
                cmd.CommandText = "declare @empty as dbo.KrjVarBin; execute dbo.bob @empty";
            }

            cmd.ExecuteNonQuery();

        }


来源:https://stackoverflow.com/questions/35782496/empty-datatable-causes-errors-when-table-valued-parameter-has-varbinary-types

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!