鉴于下表:
create table egyen (
id number not null,
tajszam varchar2(9),
nev varchar2(10),
primary key(id)
);
以下包:
create or replace package pck_egyen is
type egyentab is table of egyen%rowtype;
procedure list(ret$ out egyentab);
end pck_egyen;
/
create or replace package body pck_egyen is
procedure list(ret$ out egyentab) is
begin
ret$ := egyentab();
for rec in (select * from egyen) loop
ret$.extend;
ret$(ret$.count) := rec;
end loop;
end;
end pck_egyen;
/
有人可以使用ODP.NET 11g为我提供可用的.NET代码,可以调用此存储过程,而无需以任何方式更改表或类型或包吗?我不知道如何设置OracleParameter以支持pck_egyen.egyentab类型......
之前我没有使用过UDT,我试了一下但它不会识别包中嵌入的类型。全局类型不支持%rowtype。好像我被卡住了。最初我想返回ref游标,就像魅力一样,但不是通过dblink。太多愚蠢的限制。
答案 0 :(得分:2)
一种方法是使用一些PL / SQL将表对象中的数据读入PL / SQL关联数组,每列一个,然后使用ODP.NET读取它们:
private const string PlSqlBlock = @"
DECLARE
l_egyen_tab pck_egyen.egyentab;
BEGIN
pck_egyen.list(l_egyen_tab);
FOR i IN 1..l_egyen_tab.COUNT
LOOP
:ids(i) := l_egyen_tab(i).id;
:tajszams(i) := l_egyen_tab(i).tajszam;
:nevs(i) := l_egyen_tab(i).nev;
END LOOP;
END;";
public static void ListEgyenTable(OracleConnection con)
{
using (var cmd = new OracleCommand(PlSqlBlock, con))
{
OracleParameter idParam = cmd.Parameters.Add("ids", OracleDbType.Decimal);
OracleParameter tajszamParam = cmd.Parameters.Add("tajszams", OracleDbType.Varchar2);
OracleParameter nevParam = cmd.Parameters.Add("nevs", OracleDbType.Varchar2);
int arraySize = 1000;
int[] varcharArrayBindSize = Enumerable.Repeat(4000, arraySize).ToArray();
foreach (OracleParameter param in cmd.Parameters)
{
param.Direction = ParameterDirection.Output;
param.CollectionType = OracleCollectionType.PLSQLAssociativeArray;
param.Size = arraySize;
if (param.OracleDbType == OracleDbType.Varchar2)
{
param.ArrayBindSize = varcharArrayBindSize;
}
}
cmd.ExecuteNonQuery();
if (idParam.Value is OracleDecimal[] && tajszamParam.Value is OracleString[] && nevParam.Value is OracleString[])
{
List<decimal> ids = (idParam.Value as OracleDecimal[]).Select(dec => dec.Value).ToList();
List<string> tajszams = (tajszamParam.Value as OracleString[]).Select(str => str.Value).ToList();
List<string> nevs = (nevParam.Value as OracleString[]).Select(str => str.Value).ToList();
for (int i = 0; i < ids.Count; ++i)
{
Console.WriteLine("Got id {0}, tajszam {1}, nev {2}", ids[i], tajszams[i], nevs[i]);
}
}
else
{
Console.WriteLine("Sorry, returned data not as expected :(");
}
}
}
这里唯一的复杂因素是arraySize
。此值至少必须是从存储过程返回的行数,如果它太小,您将获得ORA-06513'PL / SQL:PL / SQL表的索引超出宿主语言的范围数组'错误。
我创建了你的表和包,并在表中插入了以下测试数据:
SQL> select * from egyen;
ID TAJSZAM NEV
---------- --------- ----------
1 abc defg
2 def mnop
3 ghi qrstu
4 jkl vwxyz
当我运行上面的C#代码时,我得到以下输出:
Got id 1, tajszam abc, nev defg Got id 2, tajszam def, nev mnop Got id 3, tajszam ghi, nev qrstu Got id 4, tajszam jkl, nev vwxyz
答案 1 :(得分:2)
这是一个有趣的问题,特别是因为需要使用.NET来跨dblinks使用数据和存储过程。不使用游标跨dblinks的限制导致你进入pl / sql表,但是你不能在.NET中轻松使用它们(不需要经历设置和维护全局对象的痛苦)。
因此,我建议将ref cursor函数和过程与输出表参数一起使用。您将能够轻松地从.NET调用ref游标函数(不需要在dblinks中选择),对于dblink数据库工作,请使用相应的过程。例如:
create table test1 (
col1 number,
col2 varchar2(10),
col3 date default sysdate not null
);
insert into test1(col1,col2) values (1,'A');
insert into test1(col1,col2) values (1,'X');
insert into test1(col1,col2) values (2,'B');
commit;
CREATE OR REPLACE package TEST_PKG as
type t_test1_tab is table of test1%rowtype;
-- weak ref cursor
function get_test1_cur (i_num in number) return sys_refcursor;
-- uses rowtype for table
procedure get_test1_tab(i_num in number, o_tab out t_test1_tab);
end;
CREATE OR REPLACE package body TEST_PKG as
function get_test1_cur (i_num in number) return sys_refcursor is
l_cur sys_refcursor;
begin
open l_cur for select * from test1 where col1=i_num;
return l_cur;
end;
procedure get_test1_tab(i_num in number, o_tab out t_test1_tab) is
l_rec test1%rowtype;
l_tab t_test1_tab := t_test1_tab();
l_cur sys_refcursor;
begin
l_cur := get_test1_cur(i_num);
loop
fetch l_cur into l_rec;
exit when l_cur%notfound;
l_tab.extend;
l_tab(l_tab.last) := l_rec;
end loop;
close l_cur;
o_tab := l_tab;
end;
end;
在ref游标函数中放置所需的任何逻辑。该过程只是调用函数并创建表(使用rowtype)。
跨dblinks使用db调用的过程:
declare
l_tab test_pkg.t_test1_tab@dblinkA;
begin
test_pkg.get_test1_tab@dblinkA(1, l_tab);
-- show count
dbms_output.put_line('Table has ' || l_tab.count || ' rows.');
end;
使用odp.net调用函数。例如(代码段):
OracleConnection con = new OracleConnection(connStr);
// create the command object and set attributes
OracleCommand cmd = new OracleCommand("test_pkg.get_test1_cur", con);
cmd.CommandType = CommandType.StoredProcedure;
cmd.BindByName = false;
// create parameter object for the cursor
OracleParameter p_refcursor = new OracleParameter();
// create any input parameters to the function
OracleParameter p_num = new OracleParameter();
p_refcursor.OracleDbType = OracleDbType.RefCursor;
p_refcursor.Direction = ParameterDirection.ReturnValue;
cmd.Parameters.Add(p_refcursor);
// add any input parameters
p_num.OracleDbType = OracleDbType.Int32;
p_num.Direction = ParameterDirection.Input;
p_num.Value = 1;
cmd.Parameters.Add(p_num);
// create a data adapter to use with the data set
OracleDataAdapter da = new OracleDataAdapter(cmd);
// create the data set
DataSet ds = new DataSet();
// fill the data set
da.Fill(ds);
这种方法应该易于维护,因为你不仅使用rowtype,而且程序只是调用函数。
答案 2 :(得分:0)
尝试类似
的内容OracleCommand cmd = new OracleCommand();
cmd.CommandText = "pck_egyen.list";
cmd.CommandType = CommandType.StoredProcedure;
cmd.Connection = con;
cmd.Parameters.Add("ret$", OracleDbType.RefCursor, DBNull.Value, ParameterDirection.Output);
cmd.ExecuteNonQuery();
// Read the result set
OracleRefCursor orarefcur = (OracleRefCursor) cmd.Parameters[0].Value;
OracleDataReader dr = orarefcur.GetDataReader();
while (dr.Read())
{
System.Console.WriteLine(dr["tajszam"]);
}