在.NET中調(diào)用Oracle9i存儲(chǔ)過(guò)程可以用多個(gè)數(shù)據(jù)提供者,比如OralceClient,OleDb。本文將用OralceClient為例。.NET語(yǔ)言用C#。
 
一、調(diào)用一個(gè)帶輸入、輸出參數(shù)的存儲(chǔ)過(guò)程
首先,在Oralce中創(chuàng)建存儲(chǔ)過(guò)程如下:
                                                                                            create or replace PRocedure GetRecords(name_out out varchar2,age_in in varchar2) as
begin
select NAME into name_out from test where AGE = age_in;
end;
 
然后,在C#項(xiàng)目中作如下調(diào)用:
     string connectionString = "Data Source=YXZHANG;User ID=YXZHANG;PassWord=YXZHANG";
     string queryString = "getrecords";
     OracleConnection cn = new OracleConnection(connectionString);
     OracleCommand cmd = new OracleCommand(queryString,cn);
     cmd.CommandType = CommandType.StoredProcedure;
     cmd.Parameters.Add("name_out",OracleType.VarChar,20);
     cmd.Parameters["name_out"].Direction = ParameterDirection.Output;
     cmd.Parameters.Add("age_in",21);
     
     try
     {
         cn.Open();
         cmd.ExecuteNonQuery();
         Console.WriteLine("Name is:{0}",cmd.Parameters["name_out"].Value.ToString());
         cn.Close();
     }
     catch( OracleException ex )
     {
         Console.WriteLine("Exception occurred!");
         Console.WriteLine("The exception message is:{0}",ex.Message.ToString());
     }
     finally
     {
         Console.WriteLine("------------------End-------------------");
     }
小結(jié):
以上是很平常的調(diào)用方法,但是我在做這個(gè)示例程序的時(shí)候,卻不是那么一帆風(fēng)順。這里要指出OracleClient的一個(gè)不盡如人意之處,就是對(duì)于參數(shù)名稱,必須和存儲(chǔ)過(guò)程定義中的參數(shù)同名,否則就會(huì)報(bào)錯(cuò)。比如將代碼中的“name_out”改為別的名稱的話,就會(huì)報(bào)異常。但我試著用OleDb代替OralceClient,卻沒(méi)有這個(gè)問(wèn)題。不知道在新版本的數(shù)據(jù)提供程序中會(huì)否改進(jìn)這一點(diǎn)?
二、調(diào)用不返回?cái)?shù)據(jù)的存儲(chǔ)過(guò)程
首先,在Oralce中創(chuàng)建存儲(chǔ)過(guò)程如下:
create or replace procedure insertRecord(UserID in varchar2,
UserName in varchar2,
                                         UserAge in varchar2) is
    begin
      insert into test values (UserID, UserName, UserAge);
    end;
 
然后,在C#項(xiàng)目中作如下調(diào)用:
 
                         
string connectionString = "Data Source=YXZHANG;User ID=YXZHANG;Password=YXZHANG";
string queryString = "insertRecord";
OracleConnection cn = new OracleConnection(connectionString);
OracleCommand cmd = new OracleCommand(queryString,cn);
cmd.CommandType = CommandType.StoredProcedure;
     cmd.Parameters.Add("UserID","007");
     cmd.Parameters.Add("UserName","Dell");
     cmd.Parameters.Add("UserAge","40");
              
     try
     {
         cn.Open();
         cmd.ExecuteNonQuery();
         Console.WriteLine("Record inserted!");
         cn.Close();
     }
     catch( OracleException ex )
     {
         Console.WriteLine("Exception occurred!");
         Console.WriteLine("The exception message is:{0}",ex.Message.ToString());
     }
     finally
     {
         Console.WriteLine("------------------End-------------------");
     }
 
 
小結(jié):
       不返回?cái)?shù)據(jù)的存儲(chǔ)過(guò)程一般有Delete, Insert, Update等。雖然它們的SQL語(yǔ)句不同,但是調(diào)用方面都是一樣的。只要傳入正確的參數(shù),一般不會(huì)出什么問(wèn)題。不過(guò)還是要注重,在使用OralceClient作為數(shù)據(jù)提供者的時(shí)候,參數(shù)名稱一定要和存儲(chǔ)過(guò)程定義中的一致!至于參數(shù)添加的順序倒是無(wú)所謂的,因?yàn)橛袇?shù)名稱作為區(qū)別。
 
三、IDENTITY 和 SEQUENCE
Sql Server中,定義一個(gè)列為遞增列很輕易,但我在Oracle中卻怎么也找不到設(shè)置的方法。不過(guò)查了點(diǎn)資料后知道Oracle中有個(gè)叫Sequence的對(duì)象,產(chǎn)生一個(gè)唯一的序列號(hào),類似于Sql Server中的IDENTITY。于是,我做了如下實(shí)驗(yàn):
 
首先,在Oracle中創(chuàng)建了一個(gè)名為TEST_SEQ的Sequence對(duì)象,SQL語(yǔ)句如下:
create sequence TEST_SEQ
minvalue 100
maxvalue 999
start with 102
increment by 1
nocache;
     
語(yǔ)法應(yīng)該是比較易懂的,最小最大值分別用minvalue,maxvalue表示,初始值是102(這個(gè)數(shù)字是動(dòng)態(tài)變化的,我創(chuàng)建的時(shí)候設(shè)的是100,后因插入了2條數(shù)據(jù)后就自動(dòng)增加了2),increment當(dāng)然就是步長(zhǎng)了。在PL/SQL中可以用test_seq.nextval訪問(wèn)下一個(gè)序列號(hào),用test_seq.currval訪問(wèn)當(dāng)前的序列號(hào)。
 
    定義完了Sequence,接下來(lái)就是創(chuàng)建一個(gè)存儲(chǔ)過(guò)程InsertRecordWithSequence:
--這次我修改了test表的定義,和前面的示例不同。其中,UserID是PK。
create or replace procedure InsertRecordWithSequence(UserID   out number,
                                                         UserName in varchar2,
                                                         UserAge  in number) 
is
begin
         insert into test(id, name, age)         --插入一條記錄,PK值從Sequece獲取
         values(test_seq.nextval, UserName, UserAge);
/*返回PK值。注重Dual表的用法*/
select test_seq.currval into UserID from dual;    
end InsertRecordWithSequence;
                         
    
接下來(lái),就是在C#中進(jìn)行調(diào)用了:
string connectionString = "Data Source=YXZHANG;User ID=YXZHANG;Password=YXZHANG";
     string queryString = "InsertRecordWithSequence";
     OracleConnection cn = new OracleConnection(connectionString);
     OracleCommand cmd = new OracleCommand(queryString,cn);
     cmd.CommandType = CommandType.StoredProcedure;
     cmd.Parameters.Add("UserID",OracleType.Number);
     cmd.Parameters["UserID"].Direction = ParameterDirection.Output;
     cmd.Parameters.Add("UserName","Jack");
     cmd.Parameters.Add("UserAge",40);
     
     try
     {
          cn.Open();
          int rowAffected = cmd.ExecuteNonQuery();
          Console.WriteLine("{0}行已插入。",rowAffected);
          Console.WriteLine("插入行的ID為:{0}",cmd.Parameters["UserID"].Value.ToString());
         cn.Close();
     }
     catch( OracleException ex )
     {
         Console.WriteLine("Exception occurred!");
         Console.WriteLine("The exception message is:{0}",ex.Message.ToString());
     }
     finally
     {
         Console.WriteLine("------------------End-------------------");
     }
小結(jié):
    使用Sequece對(duì)象可以很輕易地創(chuàng)建唯一序列,在存儲(chǔ)過(guò)程中的調(diào)用也十分方便,只要sequence_name.nextval以及sequence.currval就能得到下一個(gè)以及當(dāng)前的序列值。倒是Dual表值得注重。
 
四、使用DataReader讀取返回的結(jié)果集
為了讓存儲(chǔ)過(guò)程返回結(jié)果集,必須定義一個(gè)游標(biāo)變量作為輸出參數(shù)。這和Sql Server中有著很大的不同!并且還要用到Oracle中“包”(Package)的概念,似乎有點(diǎn)繁瑣,但熟悉后也會(huì)覺(jué)得很方便。
關(guān)于“包”的概念,有很多內(nèi)容可以參考,在此就不贅述了。首先,我創(chuàng)建了一個(gè)名為TestPackage的包,包頭是這么定義的: