2014-05-23 42 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 –