从 VB.NET 调用 Oracle 过程仅在前 2 次有效

Posted

技术标签:

【中文标题】从 VB.NET 调用 Oracle 过程仅在前 2 次有效【英文标题】:Calling Oracle Procedure from VB.NET only works the first 2 times 【发布时间】:2012-08-31 20:01:03 【问题描述】:

我正在处理一个读取文件的项目,为文件的每一行调用 Oracle 存储过程,根据过程返回的值重命名文件,并将文件移动到服务器。我已经完成了所有这些工作,但由于某种原因,该过程仅在前两次被调用时返回一个值。在那之后,我什么也得不到。我尝试关闭循环中的连接并重新打开它,但这无济于事。我也真的不想这样做。

已更改名称以保护无辜的服务器。

Do While Not endOfData
    Dim dataRecord As String = dataReader1.GetRecord
    If dataRecord = "End of Data" Then
        endOfData = True
    Else
        ' Read in record '
        indexRec = indexLoader.LoadPointerIndex(dataRecord, PointerIndexRecordType.Original)

        'Format date values '
        effdate = indexRec.EffDate.ToString("M/dd/yyyy")
        dir = "/" & easyFile & "/" & indexRec.EffDate.Year & "/" & indexRec.EffDate.Month & "/"


        'Connect to Oracle '
        Dim conn As New OracleConnection(connString)
        conn.Open()
        'Call proc with indexRec values '
        Dim myCMD As New OracleCommand()
        myCMD.Connection = conn
        myCMD.CommandText = "SCHEMA.PROC"
        myCMD.CommandType = CommandType.StoredProcedure
        'Setup Parameters '
        myCMD.Parameters.Add("P_CONTRACT_ID", OracleDbType.Varchar2, 10, Nothing, ParameterDirection.Input).Value = indexRec.Contract
        myCMD.Parameters.Add("P_EFFT_DATE", OracleDbType.Date, ParameterDirection.Input).Value = indexRec.EffDate
        myCMD.Parameters.Add("P_DIRECTORY", OracleDbType.Varchar2, 18, Nothing, ParameterDirection.Input).Value = dir
        myCMD.Parameters.Add("P_FILENAME_OUT", OracleDbType.Varchar2, 13, Nothing, ParameterDirection.Output)

        myCMD.ExecuteNonQuery()

        Result = CType(myCMD.Parameters("P_FILENAME_OUT").Value.ToString, String)
        myCMD.Dispose()
        conn.Close()
        conn.Dispose()

        'Rename PDF based on returned result '
        My.Computer.FileSystem.RenameFile(rootDirectory & "\" & configSettings.appFolder & "\" & indexRec.Contract & ".pdf", Result)

        'Verify that folder exists on server '
        sFolder = sDir & indexRec.EffDate.Year & "\" & indexRec.EffDate.Month
        If (Not Directory.Exists(sFolder)) Then
            Directory.CreateDirectory(sDir & indexRec.EffDate.Year & "\" & indexRec.EffDate.Month)
        End If

        'Move PDF to proper folder '
        My.Computer.FileSystem.CopyFile(rootDirectory & "\" & configSettings.appFolder & "\" & Result, sFolder & "\" & Result)
    End If
Loop

这是另一个团队管理的 Proc。

create or replace
PROCEDURE PROC(P_CONTRACT_ID  IN VARCHAR2,
               P_EFFT_DATE    IN DATE,
               P_DIRECTORY    IN VARCHAR2,
               P_FILENAME_OUT OUT VARCHAR2)
--*****************************************************
    -- Name: PROC
    -- Purpose: Insert a contract statement                                       
    -- Authored by: 
    -- Created on: 
    -- Modifications:
    -- Seq Who Date Description
    -- ----------------------------------------------------
    -- 001 DAT 08/27/2012 Initial Release
    --*****************************************************
 AS

    V_CONTRACT_UID       NUMBER;
    V_CORRESPONDENCE_UID NUMBER;
    V_DEL_COUNT          NUMBER;
    V_NEW_FILE_URL       VARCHAR2(254);

BEGIN

    --Get the contract_uid
    SELECT CONTRACT_UID
       INTO V_CONTRACT_UID
       FROM ODS.CONTRACTS
       WHERE CONTRACT_ID = P_CONTRACT_ID;

    --If existing, delete the entry and add (dont do a merge).
    SELECT COUNT(CORRESPONDENCE_UID)
      INTO   V_DEL_COUNT
      FROM   CORRESPONDENCES
      WHERE  CONTRACT_UID = V_CONTRACT_UID
             AND TRUNC(CORRESPONDENCE_CREATION_DATE) = TRUNC(P_EFFT_DATE);

    IF V_DEL_COUNT > 0 THEN
      DELETE FROM CORRESPONDENCES
        WHERE  CORRESPONDENCE_UID IN
                  (SELECT CORRESPONDENCE_UID
                     FROM   CORRESPONDENCES
               WHERE  CONTRACT_UID = V_CONTRACT_UID
                 AND TRUNC(CORRESPONDENCE_CREATION_DATE) = TRUNC(P_EFFT_DATE));
    END IF;

    -- *** Create Correspondence Row and New File Name ***
    -- *** Create new file name ***
    V_CORRESPONDENCE_UID := CORRESPONDENCES_SEQ.NEXTVAL;
    P_FILENAME_OUT       := RTRIM(LTRIM(TO_CHAR(V_CORRESPONDENCE_UID) || '.pdf'));
    V_NEW_FILE_URL       := 'http://armstorage.integritycompanies.com' || P_DIRECTORY || P_FILENAME_OUT;

    INSERT INTO CORRESPONDENCES
        (CORRESPONDENCE_UID,
         CORRESPONDENCE_TYPE,
         CORRESPONDENCE_CREATION_DATE,
         CORRESPONDENCE_CONTENT_TYPE,
         CORRESPONDENCE_IMAGE_FILE_NAME,
         CORRESPONDENCE_IMAGE_FILE_EXT,
         CORRESPONDENCE_UPDATED,
         CORRESPONDENCE_URL,
         CORRESPONDENCE_DESC,
         CONTRACT_UID)
    VALUES
        (V_CORRESPONDENCE_UID,
         'ST',
         P_EFFT_DATE,
         'application/pdf',
         P_FILENAME_OUT,
         '.pdf',
         SYSDATE,
         V_NEW_FILE_URL,
         'Contract Statement',
         V_CONTRACT_UID);

    COMMIT;

EXCEPTION
    WHEN OTHERS THEN
        DBMS_OUTPUT.PUT_LINE(SQLERRM);
        DBMS_OUTPUT.PUT_LINE(DBMS_UTILITY.FORMAT_ERROR_BACKTRACE);
        ROLLBACK;

END;

有什么建议或要求吗?

【问题讨论】:

老实说,这是一项调试工作。你试过什么?光看代码很难说。能不能把日志放到plsql代码里? 我的第一个问题是为什么您要在循环内打开/关闭/重新打开连接。如果是同一个连接,则在循环外打开它(并在循环外关闭它)。同样为了性能,您可以在循环之外设置 OracleCommand,然后在循环内部重新使用它,如果它每次都调用相同的 proc(您只需更改此时的值)。 在输出参数中没有返回任何内容表明正在引发异常。我建议更改您的异常处理程序以将异常文本填充到输出参数中 - 并在输出参数中分配足够的空间以允许发生这种情况 - 目前看起来它只有 13 个字符长。祝你好运。 您能否创建一个日志表并在每个命令之间将 proc 插入其中。您没有在 VB.NET 中捕获异常,因此可能会丢失问题 - 例如 Null 变量,当您获取 contract_uid 时,插入中的变量太大(V_NEW_FILE_URL)。 您可以尝试的另一种选择是使用 Oracle 数组绑定作为参数。而不是循环,并打开每个循环的连接(在这种情况下这实际上可能是问题),您将为循环中的每个值创建一个数组并将它们作为参数传递。我们的测试发现它们比其他保存到数据库的方法要快得多。 【参考方案1】:

Proc 正在寻找我传递的参数之一,并在另一个我无权访问的表上检查它的值。如果未找到该值,则返回“null”值。检查该问题的一些标准纠正了问题。

【讨论】:

以上是关于从 VB.NET 调用 Oracle 过程仅在前 2 次有效的主要内容,如果未能解决你的问题,请参考以下文章

如何将输出参数从 vb.net 传递到 mysql 存储过程?

在 VB.NET 中调用过程的优缺点是啥?

传递 Oracle 参数类型 PLSQLAssociativeArray 时出现 VB.NET 错误

VB.NET是如何使用ADO让存储过程返回数据表中的值呢?求解!

如何从 vb.net 中的 aspx.vb 页面调用 webmethod

从 VB.NET 调用访问宏问题