2014-01-17 76 views
0

我正在執行SQL命令以在數據庫表中創建新記錄並獲取創建的記錄的ID。但是,由VB代碼未拾取的SQL命令(未初始化的非空字段)會生成約束錯誤。該代碼大致爲: -SqlCommand.ExecuteReader無法報告錯誤

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對象爲非null,但不包含數據,並且不會生成異常。 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
Cannot insert the value NULL into column 'somecolumn', table 'tablename'; column does not allow nulls. INSERT fails.

我還增加了一個處理程序InfoMessage事件SqlConnection對象但即使當我將FireInfoMessageEventOnUserErrors設置爲true時也不會被調用。

爲什麼我沒有收到錯誤,以及確保將錯誤報告給VB的正確方法是什麼?

我正在使用Visual Studio 2008.

+0

我不希望拋出異常直到調用'reader.Read()'方法,並且它永遠不會被調用,因爲'HasRows'爲false。我正在嘗試(不成功)找到的是'HasRows'設置的點,以便提供正確的答案。您可以跳過驗證'如果reader.HasRows Then..',因爲如果它沒有行,您將永遠不會進入'While reader.Read'循環(如果沒有行,它將在第一次運行時爲假) 。 – GarethD

+0

@GarethD:嗯,我會嘗試刪除HasRows檢查,看看它是否開始產生錯誤。我會在嘗試後更新問題(週末後) – Skizz

+0

旁註:刪除第一個到If語句。他們什麼都不做。 – usr

回答

0

如果出現錯誤,則HasRows調用返回false。這樣你永遠不會看到錯誤。刪除兩個If陳述。空檢查是多餘的,另一個檢查錯誤。

0

嚴重級別是16,它不會中斷當前會話。如果你想拋出一個嚴重的錯誤,你可以你的插入後直接添加這樣的事情...

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

安全上下文必須有以執行RAISERROR WITH LOG系統管理員權限,但是如果你不能做到這一點我相信還有其他方法可以拋出一個硬性錯誤。無論如何,警告不會給你的VB代碼拋出一個錯誤。

+0

嚴重性級別16不被視爲信息,嚴重性級別10是信息性的; [MSDN States](http://technet.microsoft.com/en-us/library/aa937483%28v=sql.80%29.aspx):* 11到16的嚴重級別由用戶生成,可以是由用戶糾正* – GarethD

+0

我在看同一頁,我只是將非會話中斷級別解釋爲「信息性」,因爲他們不會拋出錯誤,但我會更改語言,因爲您是正確的,可能會產生誤導。 –

0

我不知道它究竟是如何工作的內部,但物業HasRows被稱爲像TryGetNextResult的SqlDataReader類中的私有方法,它使用了大量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 
     } 
    } 
} 

當我改變了這種嘗試插入空,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#的人比我更加自己的答案。