No cuido….

January 28, 2008

Using ODP.NET is rather hard…

Filed under: C#, ODP.NET — Tags: — nocuido @ 12:11 am

Recently I tried to use the ODP.NET provider provided by Oracle in order to connect to an Oracle 9i database. Actually, I chose ODP.NET over the Microsoft Oracle provider basically because I wanted to call a PL\SQL procedure which included a CLOB in the parameters. So, after a bit of googling, I decided to go with the safe way : use the .NET provider by Oracle. It turned out to bit a bad choice after all. The main problem was with the varchar parameters. The PL\SQL procedure is :

PROCEDURE insxml (
     username        IN VARCHAR2,      password            IN VARCHAR2,
     cvxml            IN CLOB,
     create_date        OUT DATE,
     result_int        OUT INTEGER
);

All the procedure does is insert do a lookup for the username and password and the inserted the CV clob in a table. The username and password columns were declared as VARCHAR2 20.
The C# code that I used is:

     OracleCommand command = new OracleCommand();
     command.CommandType = CommandType.StoredProcedure;
     command.CommandText = "insxml";
     command.Connection = connection;

     command.Parameters.Add("username", OracleDbType.Varchar2, ParameterDirection.Input);
     command.Parameters["username"].Size = 20;
     command.Parameters.Add("password", OracleDbType.Varchar2, ParameterDirection.Input);
     command.Parameters["password"].Size = 20;
     command.Parameters.Add("cvxml", OracleDbType.Clob, ParameterDirection.Input);
     command.Parameters.Add("create_date", OracleDbType.Date, ParameterDirection.Output);
     command.Parameters.Add("result_int", OracleDbType.Int32, ParameterDirection.Output);
     try {
             connection.Open();
             OracleGlobalization info = connection.GetSessionInfo();
             info.NumericCharacters = ".,";
             connection.SetSessionInfo(info);
             OracleClob xml = new OracleClob(connection);
             xml.Append(...);
             command.Parameters["firstname"].Value = Username;
             command.Parameters["lastname"].Value = Password;
             command.Parameters["cvxml"].Value = xml;
             command.ExecuteNonQuery();
            ....(Get the returned int)...
       } catch (Oracle.DataAccess.Client.OracleException oraex) {
               log.Error(oraex.Message, oraex);
               throw;
       } finally {
               connection.Close();
               connection.Dispose();
       }

The problem was non-deterministic… sometimes the call worked… sometimes it failed with the lovely error : Error -6502: ORA-06502: PL/SQL: numeric or value error.
At first I thought the problem was somewhere inside the procedure, so I commented out all the PL\SQL code. No luck.
Since the problem was non-deterministic I found a workaround:
use CHAR and not VARCHAR at the command parameters declaration and do a trim afterwards in the PL\SQL code.
i.e

     command.Parameters.Add("username", OracleDbType.Char, ParameterDirection.Input);
     command.Parameters["username"].Size = 20;

and in PL\SQL

PROCEDURE insxml (
     username        IN VARCHAR2,      password            IN VARCHAR2,
     cvxml            IN CLOB,
     create_date        OUT DATE,
     result_int        OUT INTEGER
) IS

   v_username VARCHAR2(20) DEFAULT '';
BEGIN
   v_username := TRIM(username)

...

After a while I came across this post :
http://forums.oracle.com/forums/thread.jspa?forumID=146&tstart=0&threadID=283165&trange=15
that suggests that:
1. all VARCHAR2 parameters must be declared in the right order and sizes must be declared explicitly too.
2. the return parameter MUST be added in the parameter collection BEFORE the input parameters.
I have tried it with output parameters but it seems to work only with ReturnValue parameters.

9 Comments »

  1. Thanks very much for this. I have had a similar problem, and its always a joy to find someone pointing out these tiny obscure issues that stop your world for a few hours/days! Adding the return parameter to the collection first was the key. I have manged to pass in a varchar2 parameter without declaring its size – this might be because its size wasn’t declared in the PL/SQL procedure either.

    Comment by Graham — May 20, 2008 @ 5:13 pm

  2. Thx a bunch
    Similar thing and I’ve already tried order thing earlier but it didn’t worked.
    Your solution is working like a charm

    Thx again

    Comment by Stjepko — July 2, 2008 @ 9:18 am

  3. Thanks heaps dude, from a guy working on this @ 2:21am in the morning!! Such a tiny issue and as Graham pointed out, can bring your working world to a grinding halt for hours / days!!

    Comment by George_Byron — July 15, 2008 @ 6:25 pm

  4. Actually, this is because by default ODP.NET does not bind parameters based on their name (like the MS Oracle client) but rather in the order they’re added (like the OleDb client). To get binding by name working for ODP.NET just do:

    cmd.BindByName = true;

    Comment by Wesley Willis — August 8, 2008 @ 10:55 am

  5. Thanks ….
    I had the same problem ..
    your page pointed me in the right direction
    Thank you very much
    Bye
    Nicola

    Comment by Nicola Farina — September 17, 2008 @ 3:16 pm

  6. Thank you for your notes! They are really helpful!!!

    Comment by Oleg — February 16, 2009 @ 2:24 pm

  7. cmd.BindByName = true;
    Fixed our problems thanks Wesley!

    Comment by Vincent Moreau — May 12, 2009 @ 4:40 pm

  8. Well, I don’t see the point why ODP.NET can’t bind OUT varchars without specifying size. IMHO this is problem of database layer (ODP.NET) to guess how big string could server return and I (programmer and user of db calls) should not be specifying this – my layer /C# code/ has string object which length is not restricted.

    Comment by joey — June 19, 2009 @ 7:09 pm

  9. Hi! I was surfing and found your blog post… nice! I love your blog. :) Cheers! Sandra. R.

    Comment by sandrar — September 10, 2009 @ 5:16 pm


RSS feed for comments on this post. TrackBack URI

Leave a comment

Blog at WordPress.com.