source

C#에서 저장 프로시저를 큰 CLOB로 호출하는 문제 발생

nicesource 2023. 6. 28. 21:47
반응형

C#에서 저장 프로시저를 큰 CLOB로 호출하는 문제 발생

제가 이러한 문제를 처음 겪는 것은 아니며, 아래에 몇 가지 참조 게시물을 나열하겠지만, 여전히 적절한 해결책을 찾고 있습니다.

C# 웹 서비스에서 저장 프로시저(Oracle 10g 데이터베이스)를 호출해야 합니다.웹 서버에 Oracle 9i 클라이언트가 설치되어 있으며 Microsoft를 사용하고 있습니다.System.Data.OracleClient.

이 절차에서는 XML을 CLOB로 사용합니다.XML이 4,000바이트 이상일 때(일반적인 사용 사례일 가능성이 높음) 다음 오류가 발생했습니다.

ORA-01460 - 구현되지 않았거나 불합리한 변환 요청

저는 이것, 이것, 그리고 이 게시물을 찾았습니다.

또한 저장 프로시저를 C#에서 직접 호출하지 않고 익명 PL/SQL 코드를 정의하는 유망한 해결 방법을 찾았습니다.이 코드는 Oracle Command로 실행됩니다.XML은 문자열 리터럴로 포함되며 프로시저 호출은 해당 코드 조각 내에서 수행됩니다.

private const string LoadXml =
    "DECLARE " +
    "  MyXML CLOB; " +
    "  iStatus INTEGER; " +
    "  sErrMessage VARCHAR2(2000); " +
    "BEGIN " +
    "  MyXML := '{0}'; " +
    "  iStatus := LoadXML(MyXML, sErrMessage); " +
    "  DBMS_OUTPUT.ENABLE(buffer_size => NULL); " +
    "  DBMS_OUTPUT.PUT_LINE(iStatus || ',' || sErrMessage); " +
    "END;";
OracleCommand oraCommand = new OracleCommand(
    string.Format(LoadXml, xml), oraConnection);
oraCommand.ExecuteNonQuery();

안타깝게XML이 32KB 이상이 되면 이 접근 방식이 실패합니다. 이는 여전히 제 애플리케이션에서 가능성이 높습니다.이번 오류는 다음과 같은 PL/SQL 컴파일러에서 발생합니다.

ORA-06550: 1행, 87열: PLS-00172: 문자열 리터럴이 너무 깁니다.

몇 가지 조사 후에 저는 제 두 번째 접근법으로는 문제를 해결할 수 없다는 결론을 내렸습니다.

위에서 언급한 게시물에 이어 다음 두 가지 옵션이 있습니다.

( 번째 게시물에는 일부 고객이 버그가 있다고 했지만, 내 고객(9i)은 언급된 10g/11g 버전 범위에 해당하지 않습니다.)

당신은 이것들이 유일하게 남은 두 가지 옵션인지 확인할 수 있습니까?아니면 저를 도와줄 다른 방법이 있나요?

XML은 결국 어떤 테이블에도 저장되지 않지만 XML 내용을 기반으로 일부 테이블에 레코드를 삽입하는 저장 프로시저에 의해 처리됩니다.

두 가지 옵션에 대한 고려 사항:

  • ODP.NET으로 전환하는 것은 지금까지 시스템 액세스 권한이 없는 웹 서버에 설치해야 하고 클라이언트에 코드를 배포해야 할 수도 있기 때문에 각 클라이언트가 배포의 일부로 ODP.NET을 설치해야 할 수도 있기 때문입니다.
  • 테이블을 우회하면 클라이언트 코드가 상당히 복잡해지고 데이터베이스가 PL/SQL 루틴을 조정/확장하는 데 상당한 노력이 소요됩니다.

저는 그 문제를 해결할 다른 방법이 있다는 것을 발견했습니다!동료 직원이 이 블로그를 가리키며 저의 하루를 저장했습니다. 이 블로그에는 다음과 같은 내용이 블로그는 다음과 같습니다.

BeginTransaction이 DbConnection에서 이미 호출된 경우 매개 변수 값을 설정합니다.

더 간단한 방법이 있을까요?이 블로그는 다음과 관련이 있습니다.Oracle.DataAccess하지만 그것도 마찬가지로 효과가 있습니다.System.Data.OracleClient.

실제로 이는 다음을 의미합니다.

varcmd = new OracleCommand("LoadXML", _oracleConnection);
cmd.CommandType = CommandType.StoredProcedure;

var xmlParam = new OracleParameter("XMLFile", OracleType.Clob);
cmd.Parameters.Add(xmlParam);

// DO NOT assign the parameter value yet in this place

cmd.Transaction = _oracleConnection.BeginTransaction();
try
{
    // Assign value here, AFTER starting the TX
    xmlParam.Value = xmlWithWayMoreThan4000Characters;

    cmd.ExecuteNonQuery();
    cmd.Transaction.Commit();
}
catch (OracleException)
{
    cmd.Transaction.Rollback();
}

저의 경우, 치코도로의 해결책은 효과가 없었습니다.ODPODP)을 .NET을 사용하고 있습니다.Oracle.DataAccess).

나를 위한 솔루션은 다음과 같습니다.OracleClob물건.

OracleCommand cmd = new OracleCommand("LoadXML", _oracleConnection);
cmd.CommandType = CommandType.StoredProcedure;

OracleParameter xmlParam = new OracleParameter("XMLFile", OracleType.Clob);
cmd.Parameters.Add(xmlParam);

//connection should be open!
OracleClob clob = new OracleClob(_oracleConnection);
// xmlData: a string with way more than 4000 chars
clob.Write(xmlData.ToArray(),0,xmlData.Length);
xmlParam.Value = clob; 

try
{
    cmd.ExecuteNonQuery();
}
catch (OracleException e)
{
}

저는 당신이 싼 포인트를 얻기 위해 이것을 구글에 검색한 것 같습니다. 하지만 여기에 훌륭한 설명이 있습니다.

http://www.orafaq.com/forum/t/48485/0/

기본적으로 문자열 리터럴에는 4000자를 초과할 수 없으며, 추가 작업이 필요한 경우 저장 프로시저를 사용해야 합니다.그러면 최대 32KB로 제한되므로 삽입을 "청크"해야 합니다.블레치.

-오이신

치코도로의 말이 맞습니다.

public static int RunProcedure(string storedProcName, IDataParameter[] parameters)
    {
        using (OracleConnection connection = new OracleConnection(connectionString))
        {
            int rowsAffected;

            OracleCommand command = new OracleCommand(storedProcName, connection);
            command.CommandText = storedProcName;
            command.CommandType = CommandType.StoredProcedure;
            foreach (OracleParameter parameter in parameters)
            {
                command.Parameters.Add(parameter);
            }
            connection.Open();

            try
            {
                // start transaction
                command.Transaction = connection.BeginTransaction();
                rowsAffected = command.ExecuteNonQuery();
                command.Transaction.Commit();
            }
            catch (System.Exception ex)
            {
                command.Transaction.Rollback();
                throw ex;
            }

            connection.Close();
            return rowsAffected;
        }
    }

언급URL : https://stackoverflow.com/questions/3557995/issues-calling-stored-procedure-from-c-sharp-with-large-clob

반응형