流男@水孩

没有人可以像你,在我的感觉里....

Making best out of Oracle batabases using ODP.net - Part 2
 
Introduction

The issue of using generic oledb.net, odbc.net layers vs. vendor specific providers has always been a debate during the development of data access layers. Second in this series this article will cover the other specific features of oracle database as used by ODP.net Please visit my earlier article Making best out of Oracle databases Using ODP.net – Part I.

Returning Multiple Result Sets from Stored Procedures

It is not uncommon for the front end development teams requesting for result sets from the stored procedures so that they need not concentrate on complex relations. The following code piece will explain one of the different ways to retrieve multiple result sets (cursors) from an odp.net client. REF CURSORS are variable cursors using which oracle allow you to pass the cursor locators between the applications.

CREATE OR REPLACE PACKAGE RefCurPkg AS
TYPE cur_emp_t IS REF CURSOR RETURN emp%ROWTYPE;
TYPE cur_dept_t IS REF CURSOR RETURN dept%ROWTYPE;
PROCEDURE get_cursor(dept_in IN NUMBER,
cur_out_emp OUT cur_emp_t,
cur_out_dept OUT cur_dept_t);
END;
/
CREATE OR REPLACE PACKAGE BODY REfCurPkg AS
PROCEDURE get_cursor(dept_in IN NUMBER,
cur_out_emp OUT cur_emp_t,
cur_out_dept OUT cur_dept_t) IS
BEGIN

OPEN cur_out_emp FOR SELECT * FROM emp
WHERE deptno = dept_in;

OPEN cur_out_dept FOR SELECT * FROM dept;
END get_cursor;
END;
/

As evident the above stored procedure returns two REF CURSORS one for the employees for a given department and the other one the entire department table.

The following ODP.net calls the stored procedure and retrieves the two result sets from it.

OracleCommand cmd = new OracleCommand();
cmd.CommandText = "REFCURPKG.GET_CURSOR";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Connection = con;
cmd.Parameters.Add("dept_in", OracleDbType.Int32, DBNull.Value, ParameterDirection.Input);
cmd.Parameters.Add("cur_out_emp", OracleDbType.RefCursor, DBNull.Value, ParameterDirection.Output);
cmd.Parameters.Add("cur_out_dept", OracleDbType.RefCursor, DBNull.Value, ParameterDirection.Output);
cmd.Parameters[0].Value = 30;

cmd.ExecuteNonQuery();
// Read the employee result set
OracleRefCursor oraref = (OracleRefCursor) cmd.Parameters[1].Value;
OracleDataReader dr = oraref.GetDataReader();
while (dr.Read())
{
System.Console.WriteLine(dr["ename"]);
}
// Read the dept result set
oraref = (OracleRefCursor) cmd.Parameters[2].Value;
dr = oraref.GetDataReader();
while (dr.Read())
{
System.Console.WriteLine(dr["dname"]);
}


As evident from the code piece OracleRefCursor is a corresponding ODP.NET for the REF CURSOR implemented in the stored procedure. The GetDataReader() from OracleRefCursor returns an OracleDataReader.

Passing C# Array Objects as Arguments To Stored Procedures

Arrays have been a popular data structure in different programming languages, unfortunately arrays have not been directly supported inside databases. However oracle has long been supporting arrays in the form of PL/SQL extensions. This support has been extended to odp.net also. The following example shows how you can pass an array object from C# client to an oracle PL/SQL stored procedure using ODP.net.

This simple example gets a list of employees as an array and returns the total salary for these employees, resulting in less network round trips.

CREATE OR REPLACE PACKAGE ArrayDemo AS
TYPE emparr_t IS TABLE OF NUMBER INDEX BY BINARY_INTEGER;
PROCEDURE get_emp_sal_sum(emparr IN emparr_t, salsum OUT NUMBER);
END;
/
CREATE OR REPLACE PACKAGE BODY arraydemo AS
PROCEDURE get_emp_sal_sum(emparr IN emparr_t, salsum OUT NUMBER) IS
curemp emp.empno%TYPE;
cursal emp.sal%TYPE;
BEGIN
salsum := 0;
FOR i IN 1..emparr.LAST
LOOP
BEGIN
curemp := emparr(i);
SELECT sal
INTO cursal
FROM emp
WHERE empno = curemp;
EXCEPTION
WHEN NO_DATA_FOUND
THEN
cursal := 0;
END;
salsum := salsum + cursal;
END LOOP;
END get_emp_sal_sum;
END;
/

The corresponding C# client to pass an array will be as follows.


OracleCommand cmd = new OracleCommand( "begin ARRAYDEMO.GET_EMP_SAL_SUM(:1, :2); end;", con);
OracleParameter Param1 = cmd.Parameters.Add("emparr",OracleDbType.Int32);
Param1.Direction = ParameterDirection.Input;
Param1.CollectionType = OracleCollectionType.PLSQLAssociativeArray;
Param1.Value = new int[4]{7900,7902,7934,7788};
Param1.Size = 4;
OracleParameter Param2 = cmd.Parameters.Add("salsum", OracleDbType.Int32, ParameterDirection.Output);

cmd.ExecuteNonQuery();
System.Console.WriteLine(cmd.Parameters[1].Value);

Look at the usage of OracleCollectionType.PLSQLAssociativeArray to indicate the usage of PL/SQL array. Similar concept can be applied in array binding features thru which multiple rows can be inserted into a table in a single round trip.

LOB Support

ODP.net provides the objects OracleClob to access character large object and OracleBlob to access a binary large object stored inside the database. The following example loads a Word Resume into an employee table and then retrieves the same document to create a new word document. Internally OracleBlob is represented as System.Byte[].

Let us first create a table in oracle with a BLOB column, in this simple example employee’s information are stored along with their resume.

CREATE TABLE empinfo
(
eno NUMBER(4) ,
ename VARCHAR2(10) ,
resume BLOB
)
TABLESPACE USERS;

The following code piece will create an employee record and will load a resume from the local file system.

OracleCommand cmd = new OracleCommand();
cmd.CommandText = "INSERT INTO empinfo(eno, ename, resume) VALUES (:eno,:ename,:resume) ";
cmd.Connection = con;
cmd.Parameters.Add(":eno", OracleDbType.Int32 );
cmd.Parameters.Add(":ename", OracleDbType.Varchar2);
cmd.Parameters.Add(":resume", OracleDbType.Blob);
cmd.Parameters[0].Value = 100;
cmd.Parameters[1].Value = "WILL SMITH";
System.IO.FileStream fs =
new System.IO.FileStream("C://FILES TO MOVE//docs//resume.doc",
System.IO.FileMode.Open, System.IO.FileAccess.Read);
byte[] resBytes = new byte[fs.Length];
fs.Read(resBytes, 0, resBytes.Length);
fs.Close();
cmd.Parameters[2].Value = resBytes;
cmd.ExecuteNonQuery();

Having loaded the resume into the database, the following code piece will retrieve the employee record and will create a word document from the OracleBlob column.

System.IO.FileStream fw =
new System.IO.FileStream("C://FILES TO MOVE//docs//resumenew.doc",
System.IO.FileMode.Create, System.IO.FileAccess.Write);
BinaryWriter br = new BinaryWriter(fw);
cmd.CommandText = "SELECT eno, ename, resume FROM empinfo";
OracleDataReader dr = cmd.ExecuteReader();
OracleBlob readResume = new OracleBlob(con);

while (dr.Read())
{
Console.WriteLine(dr["ename"]);
readResume = dr.GetOracleBlob(2);
}
br.Write(readResume.Value);
dr.Close();

Conclusion

This section of the series demonstrates further the ability of ODP.net in developing database applications on oracle databases. The features like LOB, multi result sets from stored procedures are certainly essential for any serious database development projects. I will try to cover mode advanced features in the coming articles.
阅读更多
个人分类: WINFORM
想对作者说点什么? 我来说一句

Tin Can API

2014年03月25日 1.71MB 下载

没有更多推荐了,返回首页

不良信息举报

Making best out of Oracle batabases using ODP.net - Part 2

最多只允许输入30个字

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭