2014-05-23 48 views
0

一個Oracle更新存儲過程我有一個封裝內的2個甲骨文prodecures這樣的:調用從C#

PROCEDURE INSERT_LOG (
    message_id  in VARCHAR2, 
    mq_request  in VARCHAR2, 
    req_timestamp in VARCHAR2 
) 
IS 
BEGIN 
    INSERT INTO TESTSCHEMA.MY_MESSAGE_LOG (MESSAGE_ID,MQ_REQUEST,REQ_TIMESTAMP) 
    VALUES(message_id,mq_request,TO_DATE(req_timestamp,'DD-MM-YYYY HH24:MI:SS')); 
    commit; 
EXCEPTION 
    WHEN OTHERS 
    THEN 
    ROLLBACK; 
    RAISE_APPLICATION_ERROR (-20000, 'Error: INSERT_LOG() '||SQLERRM); 
END; 

PROCEDURE UPDATE_LOG (
     message_id  IN VARCHAR2, 
     mq_response  IN VARCHAR2, 
     resp_identifier IN VARCHAR2, 
     resp_timestamp IN VARCHAR2, 
     req_timestamp IN VARCHAR2 
     ) 
IS 
BEGIN 
     UPDATE TESTSCHEMA.MY_MESSAGE_LOG A 
     SET 
      A.MQ_RESPONSE = mq_response, 
      A.RESP_IDENTIFIER =resp_identifier, 
      A.RESP_TIMESTAMP = TO_DATE(resp_timestamp,'DD-MM-YYYY HH24:MI:SS') 
     WHERE  
      A.MESSAGE_ID = message_id 
      and A.REQ_TIMESTAMP = TO_DATE(req_timestamp,'DD-MM-YYYY HH24:MI:SS'); 

     commit; 
EXCEPTION 
    WHEN OTHERS 
    THEN 
     ROLLBACK; 
     RAISE_APPLICATION_ERROR (-20000, 'Error: UPDATE_LOG() '||SQLERRM); 
END; 

我試圖從我的C#代碼中調用這些過程。 問題是插入過程工作正常,但更新過程不更新任何行。在調查時,我注意到雖然我從C#代碼設置存儲過程的變量,但這些變量的值在數據庫端反映爲空或空。這是我的更新過程的C#代碼。

try 
     { 
      using (DbConnection connection = new DbConnection()) 
      { 
       var comm = new OracleCommand 
       { 
        Connection = connection.OpenUsbAppsSchema(), 
        CommandText = <My Procedure Name>, 
        CommandType = CommandType.StoredProcedure 
       }; 

       var param = new OracleParameter[5]; 

       param[0] = new OracleParameter("message_id", OracleDbType.Varchar2, 500, ParameterDirection.Input) { Value = messageId }; 
       param[1] = new OracleParameter("mq_response", OracleDbType.Varchar2, 2000, ParameterDirection.Input) { Value = mqResponse }; 
       param[2] = new OracleParameter("resp_identifier", OracleDbType.Varchar2, 200, ParameterDirection.Input) { Value = identifier }; 
       param[3] = new OracleParameter("resp_timestamp", OracleDbType.Varchar2,500, ParameterDirection.Input) { Value = resposeTimeStamp }; 
       param[4] = new OracleParameter("req_timestamp", OracleDbType.Varchar2, 500, ParameterDirection.Input) {Value = requestTimeStamp}; 

       comm.Parameters.AddRange(param); 
       comm.ExecuteNonQuery(); 

      } 
     } 
     catch (Exception ex) 
     { 
      throw ex; 
     } 

什麼是我做錯了什麼指針?

+0

JustinCave的答案是點亮的。直到我看到[那個邪惡的'catch'子句](http://stackoverflow.com/a/881489/158074),我纔會說C#方面沒有錯。除此之外,一切都是正確的,你只需要改變'UPDATE_LOG'上的參數名稱...... – rsenna

回答

2

通常,您不希望將過程的參數命名爲可能與表中的列名衝突的事情。這使得你很容易犯這樣的錯誤,你在這裏看到的是當Oracle實際上將它們解析爲表中的列時,你期望標識符引用參數。

看着你UPDATE聲明,

UPDATE TESTSCHEMA.MY_MESSAGE_LOG A 
    SET A.MQ_RESPONSE = mq_response, 
     A.RESP_IDENTIFIER =resp_identifier, 
     A.RESP_TIMESTAMP = TO_DATE(resp_timestamp,'DD-MM-YYYY HH24:MI:SS') 
WHERE A.MESSAGE_ID = message_id 
    and A.REQ_TIMESTAMP = TO_DATE(req_timestamp,'DD-MM-YYYY HH24:MI:SS'); 

當Oracle看到獨立標識符mq_response,它首先要解決的是,在表中使用的列。由於表中有mq_response列,這就是Oracle使用的。它不會查看是否存在名爲mq_response的本地變量或名爲mq_response的參數,除非檢查表中的列失敗。這發生在你的查詢中的每個不合格的標識符,以便查詢最終被(邏輯)這個查詢其更新的每行的表,但不改變任何值

UPDATE TESTSCHEMA.MY_MESSAGE_LOG A 
    SET A.MQ_RESPONSE = a.mq_response, 
     A.RESP_IDENTIFIER =a.resp_identifier, 
     A.RESP_TIMESTAMP = TO_DATE(a.resp_timestamp,'DD-MM-YYYY HH24:MI:SS') 
WHERE A.MESSAGE_ID = a.message_id 
    and A.REQ_TIMESTAMP = TO_DATE(a.req_timestamp,'DD-MM-YYYY HH24:MI:SS'); 

最常見的方式來解決,這是對採用參數和局部變量的標準命名約定來區分它們與表中的列。就個人而言,我使用p_作爲參數的前綴,並使用l_作爲局部變量的前綴,這是很常見的。如果你這樣做,你會有類似

PROCEDURE UPDATE_LOG (
     p_message_id  IN VARCHAR2, 
     p_mq_response  IN VARCHAR2, 
     p_resp_identifier IN VARCHAR2, 
     p_resp_timestamp IN VARCHAR2, 
     p_req_timestamp IN VARCHAR2 
     ) 
IS 
BEGIN 
     UPDATE TESTSCHEMA.MY_MESSAGE_LOG A 
      SET A.MQ_RESPONSE  = p_mq_response, 
       A.RESP_IDENTIFIER = p_resp_identifier, 
       A.RESP_TIMESTAMP = TO_DATE(p_resp_timestamp,'DD-MM-YYYY HH24:MI:SS') 
     WHERE A.MESSAGE_ID = p_message_id 
      and A.REQ_TIMESTAMP = TO_DATE(p_req_timestamp,'DD-MM-YYYY HH24:MI:SS'); 

     commit; 
EXCEPTION 
    WHEN OTHERS 
    THEN 
     ROLLBACK; 
     RAISE_APPLICATION_ERROR (-20000, 'Error: UPDATE_LOG() '||SQLERRM); 
END; 

另一種方法是顯式限定查詢中的所有參數名稱。你可以通過使用你的函數作爲限定符的名稱,即

UPDATE TESTSCHEMA.MY_MESSAGE_LOG A 
    SET A.MQ_RESPONSE = update_log.mq_response, 
     A.RESP_IDENTIFIER =update_log.resp_identifier, 
     A.RESP_TIMESTAMP = TO_DATE(update_log.resp_timestamp,'DD-MM-YYYY HH24:MI:SS') 
WHERE A.MESSAGE_ID = update_log.message_id 
    and A.REQ_TIMESTAMP = TO_DATE(update_log.req_timestamp,'DD-MM-YYYY HH24:MI:SS'); 

就個人而言,我覺得前綴是更可讀和更容易出錯。

看看您的代碼,我強烈建議您取出commit語句並刪除異常處理程序。如果在一個過程中有commit語句,那麼這些過程立即變得遠不能重複使用,因爲如果一段代碼處於事務中間,它們就無法使用。否則,你的commit將承諾調用者可能正在執行的所有工作。如果你的代碼可以對異常做一些有用的事情,或者你的自定義異常將爲調用者提供更多信息,那麼異常處理程序只應該捕獲異常。但是,在這種情況下,您的所有異常處理程序都在拋出錯誤堆棧,並阻止調用方輕鬆檢查錯誤代碼以確定發生了什麼問題。你可能不會失去太多的代碼調試信息,但這會很簡單,但是如果你編寫像這樣的異常處理程序,你可以設置一個麻煩的世界,試圖調試你的代碼。

+0

謝謝賈斯汀!這工作,現在我理解它背後的原因。 Cheers,Kunal –