WorkaHolic/ORACLE

.NET에서 Oracle과 DataAdapter를 사용시 주의할 몇가지 사항

2008. 2. 15. 16:08
반응형

출처 [고수닷넷 : http://www.gosu.net/GosuWeb/Article-detail.aspx?ArticleCode=923]

[Oracle Episode]


제가 .NET과 Oracle을 가지고 Project를 하면서 발생했던 문제점에 대해서 간단하게 설명드리겠습니다.


제가 있는 회사는 도서관 관련 소프트웨어를 개발해서 납품하는 솔루션 회사인 관계로 각각의 도서관마다 DBMS를 맞추어 주어야 하는 문제를 안고 있는 곳입니다. 그래서 몇몇 RDBMS의 특성과 SQL문을 사용할 때 특성을 잘 알아야만 각각에 맞게 대처를 할 수 있는 특수한 상황입니다.


그리하여 RDBMS에 관계없이 Provider를 적절하게 선택해 주어 명령객체를 쉽게 만들어주는 Wrapper Block같은 것을 직접 개발해서 적용하고 있습니다.

결국 직접 Command객체를 사용하기 보다는 IDbCommand같은 인터페이스를 이용해서 주로 작업을 하게 됩니다.



제가 K모 학교 프로젝트를 진행하는데, 거기는 HP장비에 Oracle을 탑재해서 사용한다고 하더군요.


그런데 제가 .NET + MSSQL, .NET + INFORMIX 이렇게는 프로젝트를 수행해 본 경험이 있는지라 .NET + Oracle를 이용해 본것은 이번이 처음이었습니다.


VB를 사용하던 시절에는 크게 문제가 되지 않았던 사소한 사항들이 .NET에 와서는 문제가 되어서 저를 괴롭혔습니다. ^^;


다음은 모두 MS에서 제공하는 System.Data.OracleClient를 이용한 것이 아니라 Oracle에서 제공하는 ODP.NET (Oracle Data Provider)을 이용한 사항들입니다.


문제.1 (사소한 문제)


Oracle을 이용해 보신 분들은 Oracle에서 LONG형 컬럼을 잘 아실것입니다.

CREATE TABLE foo
(
    ID NUMBER,
    BODY LONG
);

위와 같은 스크립트로 간단하게 작성된 테이블이 있다고 할 때, 우리는 다음과 같은 방식으로 Command를 작성하게 됩니다.

OracleConnection cn = new OracleConnection("data source=DATASOURCE;user id=scott;password=tiger");

            cn.Open();

            OracleCommand cmd = cn.CreateCommand();

            cmd.CommandText = "SELECT * FROM foo";

            cmd.CommandType = CommandType.Text;

            OracleDataReader dr = cmd.ExecuteReader();

            while (dr.Read())

            {

                System.Diagnostics.Debug.WriteLine(string.Format("{0} --> {1}", dr["id"], dr["body"]));

            }

            dr.Close();

            cn.Close();

            cn.Dispose();

            cn = null;

대충 위와 같은 코딩이면 결과가 나올것입니다. 라고 생각하시면 오산이고요.

그러나 우리가 전혀 생각하지 방식으로 데이터가 나오게 됩니다. (실험해 보시기 바랍니다.)

1 --> 

이렇게 LONG형 데이터를 못가져 오는 일이 생기게 됩니다.

이렇게 LONG형 데이터를 가져오지 못하는 일이 왜 생기는지... 지인들에게 묻고 또 물어, Oracle에 문의한 결과 Oracle에서는 기본옵션으로는 LONG형 컬럼을 패치해 내지 않는다고 합니다.

cmd.InitialLOBFetchSize = -1;

cmd.InitialLONGFetchSize = -1;

OracleCommand객체에 있는 위의 두가지 옵션을 -1로 설정을 해야만 무조건 전체를 패치해 낸다고 하더군요.

저희는 위에서 언급했다시피, Interface를 가지고 작업을 하는지라, 저 옵션을 주려고 고민하던 끝에 Reflection을 이용하여 다음과 같이 처리를 하게 되었습니다.

PropertyInfo info = command.GetType().GetProperty("InitialLONGFetchSize");

if (info == null)

    return;

info.GetSetMethod().Invoke(command, new object[] { -1 });

이리하여 첫 문제를 해결하게 되었습니다.


문제.2 (어려운 문제)


위의 문제를 해결하고 잘 개발하던중에 Oracle RDBMS에서 이상한 메시지를 받게 되었습니다.

[열려있는 커서의 최대 개수를 초과했습니다.]

라는 난생 보지도 못했던 오류였죠.

위의 오류 메시지를 보면 보통

"아 누군가 DataReader를 이용해서 커서를 열고 Close를 하지 않았나보다.."

라고 생각하실 것입니다.


며칠을 걸려 전체 소스 코드중에 DataReader를 Close하지 않은 부분을 뒤졌습니다. 그러나 나오지 않았습니다. Wrapper를 작성하고 그 안에서 대부분의 처리를 다 하였고, DataReader보다는 DataAdapter를 사용하여 DataSet을 생성해 내는 코드가 대부분이었으니까요.


또 지인들에게 물어물어보고 또 Oracle에도 문의를 해보았습니다.

Oracle에서 온 답변은 어이없게도

[DataAdapter에 있는 SelectCommand객체를 Dispose하지 않은 경우에 이러한 문제가 발생할 수 있다..]

라는 답변이었습니다.

DataAdapter에 있는 SelectCommand를 Dispose해주는 코딩까지 하는 개발자가 과연 몇이나 될까요..

DataAdapter를 Dispose해주면 자동으로 Dispose되리라 생각하고 코딩을 하겠죠..


이리하여 어려운 문제를 해결했습니다. 라고 말씀드리면 서운합니다. ^^;

그런데 더 웃긴 문제가 발생했습니다.

위의 문제를 해결을 하고 커서수가 증가하지 않음을 확인하고, 계속 작업을 잘 하던중, 또 위의 문제가 발생을 하는 것이었습니다.

문제를 찾다찾다 결국 찾아낸 사항이 어이없게도...

DataAdapter를 이용해서(Command객체를 이용해도 마찬가지인지는 확인해 보지 않았습니다만)

[존재하지 않는 테이블을 조회하면 열려있는 커서수가 증가한다.]

라는 것이었습니다.


팀원중 하나가 테이블을 Drop하고 다시 안만들었는데, 마침 그 테이블을 액세스 하는 코드가 실행이 되어서 오류가 발생했는데, 그 부분을 try ~ catch만 해놓고 그 오류를 덮는 코딩을 해버려서 오류가 발생하지 않은것 처럼 되어버려서..그 부분만 통과하면 커서수가 그 회수만큼 증가하는 것이었죠.. ㅎㅎ


이렇게 해서 Oracle과의 두번의 싸움을 마치고, K모 학교를 Open할 준비를 하고 있습니다. ^^;


정리


어떤 소프트웨어던지 오류의 소지는 포함하고 있게 마련입니다. 그러나 개발자의 사소한 실수나, 뻔한 오류를 다음에 해야지 하고 미뤄 놓는 악습관을 버리고, 수많은 테스팅을 거쳐야만 쓸만한 소프트웨어가 나오는듯 합니다. 저 자신도 잘 하지 못하지만, 앞으로 하려고 노력하고 있습니다. ^^;

반응형