从C#调用oracle更新存储过程

时间:2014-05-23 17:15:25

标签: c# .net oracle

我在这样的包中有2个oracle prodecures:

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#代码设置了存储过程的变量,但这些变量的值在数据库端反映为null或empty。这是我更新过程的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;
        }

关于我做错了什么的指示?

1 个答案:

答案 0 :(得分: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将提交调用者可能正在执行的所有工作。异常处理程序应该只捕获异常,如果您的代码可以对异常执行某些有用的操作,或者您的自定义异常将向调用方提供更多信息。但是,在这种情况下,所有异常处理程序都在抛弃错误堆栈并阻止调用者轻松检查错误代码以确定出错的地方。您可能不会因为代码这么简单而丢失过多的调试信息,但是如果您对这样的异常处理程序进行编码,那么您可能会在尝试调试代码时遇到麻烦。