SqlCommand.ExecuteReader无法报告错误

时间:2014-01-17 16:23:48

标签: sql sql-server vb.net

我正在执行SQL命令以在数据库表中创建新记录并获取所创建记录的ID。但是,SQL命令(未初始化的非空字段)生成的约束错误未被VB代码拾取。代码大致是: -

connection = New SqlConnection(connection_string)
connection.Open()

sql_command = New SqlCommand(command) 'command = the SQL command to execute
sql_command.Connection = connection
sql_command.Parameters.AddRange(sql_parameters.ToArray()) ' sql_parameters is a parameter to the function

reader = sql_command.ExecuteReader()

If reader IsNot Nothing Then
    If reader.HasRows Then
        While reader.Read
            response_handler(reader, data) 'response handler is a callback which populates the data object
        End While
    End If

    reader.Close()
End If

reader对象非空,但不包含任何数据,也不会生成异常。 SQL命令是: -

insert into [table] ([column1], [column2], [column3], [column4]) 
output Inserted.[pk] 
values (@1, @2, @3, @4)

使用SQL Server Management Studio执行SQL语句,我收到错误: -

  

Msg 515,Level 16,State 2,Line 2
  无法将值NULL插入列'somecolumn',表'tablename';列不允许空值。 INSERT失败。

我还在InfoMessage对象上添加了SqlConnection事件的处理程序,但即使我将FireInfoMessageEventOnUserErrors设置为true,也不会调用该事件。

为什么我没有收到错误?确保向VB报告错误的正确方法是什么?

我正在使用Visual Studio 2008。

3 个答案:

答案 0 :(得分:0)

严重性级别为16,不会中断当前会话。如果你想抛出一个硬错误,你可以在插入后直接添加这样的东西......

If      @@Error <> 0
Begin   
        Raiserror('Constraint Error Encountered',20,1) With Log;
End

安全上下文必须具有sysadmin权限才能执行RAISERROR WITH LOG,但如果你不能这样做,我确信还有其他方法可以抛出硬错误。无论如何,警告不会给VB代码带来错误。

答案 1 :(得分:0)

我不知道它内部是如何工作的,但属性HasRows是由SqlDataReader类中的私有方法设置的,类似于TryGetNextResult,它使用了很多try / catch块设置布尔输出参数,因此此方法不会抛出任何异常,甚至提供有关任何错误的任何反馈。我无法解决的问题是它是如何实现的,即使没有尝试插入也会出现错误,但确实如此。

使用此示例表:

CREATE TABLE T (ID INT IDENTITY, A INT NOT NULL);

我跑了:

string sql = @"INSERT T (A) OUTPUT inserted.ID, inserted.A VALUES (1);";
using (var connection = new SqlConnection(connectionString))
using (var command = new SqlCommand(sql, connection))
{
    connection.Open();
    using (var reader = command.ExecuteReader())
    {
        Console.WriteLine(reader.HasRows); // True
        while (reader.Read())
        {
            Console.WriteLine(reader.GetInt32(1)); // 1
        }
    }
}

当我改变这个以尝试插入null时,reader.HasRows是假的,但是通过在我调用reader.Read()之前我可以测试一个断点,这表明了它的标识值表T没有变化,因此读者无法通过简单回滚事务来验证插入。对我来说,为什么SqlDataReader能够识别出这个插入在执行之前会失败,这是一个谜,但是如果SQL被执行,它仍然会在引发错误之前尝试插入。

为了进一步证明行为,我使用简单的select命令运行了类似的测试,并看到了相同的行为:

static void Main(string[] args)
{
    string sql = @" SELECT Date = CAST(D AS DATE)
                    FROM    (VALUES
                                (1, '20130129'),
                                (2, '20130130'),
                                (3, '20130131'),
                                (4, '20130132')
                            ) t (A, D)
                    ORDER BY A;";
    using (var connection = new SqlConnection(connectionString))
    using (var command = new SqlCommand(sql, connection))
    {
        connection.Open();
        using (var reader = command.ExecuteReader())
        {
            Console.WriteLine(reader.HasRows); // false
            while (reader.Read()) // Exception
            {
                Console.WriteLine(reader.GetString(0));
            }
        }
    }
}

再次显示,SqlDataReader在某种程度上已经发现将20130132转换为日期时会出错。如果我从SQL中删除此行,HasRows属性将变为true。

我意识到这实际上并没有回答你的问题但是评论太长了,希望它会有所帮助,并且/或者可能会提示一些比c更了解c#的人来添加他们自己的答案。

答案 2 :(得分:0)

如果出现错误,HasRows调用将返回false。这样你就永远不会看到错误。删除两个If语句。空检查是多余的,另一个是抑制错误。