【发布时间】:2016-03-03 20:35:19
【问题描述】:
我有一个 C# Web 应用程序,用作 SQL Server 的传递;请求详细的 SQL Server 命令进来,我们解析请求,生成必要的 .Net 类型,然后使用它们执行SqlCommands 等。这样做的结果是 C# Web 应用程序需要非常灵活,并且不能对请求“应该”是什么样子做出太多假设。
我最近解决了一个问题,当表值参数包含VARBINARY 类型时会引发异常。在一般情况下,我们循环传入的请求并构造一个具有适当列数和行数的DataTable。没有指定列的数据类型,数据值只是作为objects 插入的。对于VARBINARY 类型,这将导致以下错误:
不允许从数据类型 nvarchar(max) 到 varbinary(max) 的隐式转换。使用 CONVERT 函数运行此查询。
我找到了this StackOverflow post,并且能够解决问题。请看下面我的伪代码:
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);
}
}
据我所知,这很好用。当传入的 TVP 没有行时会出现问题,因为我没有要检查的数据。在这种情况下,我们只需构造一个具有正确列数的DataTable(这在有关 TVP 的请求元数据中指定),但这些列没有明确设置的数据类型。在这种情况下,DataTable 没有行。这会导致相同的异常:
不允许从数据类型 nvarchar(max) 到 varbinary(max) 的隐式转换。使用 CONVERT 函数运行此查询。
这是完整的堆栈跟踪:
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)
老实说,我根本不明白为什么会出现问题...如果DataTable 中没有数据,甚至是什么导致了无效的隐式转换?
让事情更令人沮丧的是,这个错误甚至在请求到达我的数据库之前就发生了,所以我无法使用 SQL Server Profiler 来确定发送到数据库服务器的确切内容。
这是我的问题:当相应的用户定义表类型具有 VARBINARY 字段时,如何正确地将空的 DataTable 作为表值参数传递给存储过程?在构造DataTable 时,关于表值参数的唯一信息是用户定义表类型的名称、它包含的列数、存储过程中的参数名称和值包含在请求 TVP 中,在这种情况下它本质上是一个空集。
【问题讨论】:
-
此列
Columns[colNdx]中的数据是什么,例如是时间戳...?如果是这样,则为myDataTable.Columns[colNdx].DataType =赋值 -
@MethodMan - 在问题情况下,我们甚至没有落入该代码块,因为我们没有要考虑的行。我意识到这在我最初发布的伪代码中并没有立即显现出来,因此我对其进行了修改以使其更具说明性。
-
这听起来像是一个愚蠢的问题,但是,如果您没有数据要传递,为什么还要调用存储的过程呢?如果可能,请在没有数据时跳过通话。
-
@Kevin - 不是一个愚蠢的问题,但也不是一个可接受的解决方案(-:我不能假设仅仅因为存储过程是用一个空的 TVP 参数调用的,它就不会- op;它可能仍然会产生副作用甚至返回数据。
-
我认为您应该检查以下
if(datatable.Rows.Count > 0)
标签: c# sql-server datatable varbinary table-valued-parameters