X   Сообщение сайта
(Сообщение закроется через 3 секунды)



 

Здравствуйте, гость (

| Вход | Регистрация )

Открыть тему
Тема закрыта
> Использование Oracle UDT в asp.net
Alcorn
Alcorn
Topic Starter сообщение 2.9.2010, 7:11; Ответить: Alcorn
Сообщение #1


Рассмотрим работу с пользовательскими типами данных "Oracle user defined types" (UDT) в asp.net.
Если обработка результатов происходит в самой СУБД и активно используются хранимые процедуры, что для web-приложений большая редкость, поскольку всю логику обычно выносят из БД на сервера приложений, то может возникнуть необходимость в двустороннем обмене данными между asp.net и Oracle, используя оракловские типы данных, созданные пользователем.
Итак, начнём.
Создадим новое asp.net (c#) приложение. О подключении к Oracle и выполнении sql-запросов можете прочесть в предыдущей статье.
Откроем Oracle SQL Developer и создадим три типа данных (правой кнопкой мыши Types - New Type):
Varrays (массивы).
[php]
CREATE OR REPLACE
TYPE SHOP_USER.TESTVAR AS VARRAY(100) OF VARCHAR2(50);
[/php]
Массив "testvar" из 100 значений, тип данных каждого – varchar2 (не более 50-ти символов).
Nested tables (табличный тип).
[php]
CREATE OR REPLACE
TYPE SHOP_USER.TESTTAB AS TABLE OF VARCHAR2(50);
[/php]
Табличный тип "testtab" с типом данных varchar2(50).
Objects (объекты).
[php]
create or replace
TYPE TESTOBJ AS OBJECT
(
test_id VARCHAR2(50),
MEMBER FUNCTION getnum(SELF IN TESTOBJ, paramin IN VARCHAR2) RETURN VARCHAR2
);
[/php]
Объект "testobj" с атрибутом "test_id" и методом getnum() с входящим параметром "paramin".
Теперь напишем процедуры работы с этими элементами.
Создаём package "test" и объявляем нужные процедуры –
[php]
create or replace
PACKAGE TEST AS
PROCEDURE getvar(paramin IN testvar, paramout OUT nocopy testvar);
PROCEDURE gettab(paramin IN testtab, paramout OUT nocopy testtab);
PROCEDURE getobj(paramin IN testobj, paramout OUT nocopy testobj);
TYPE mas is table of VARCHAR2(50) index by BINARY_INTEGER;
PROCEDURE getmas(paramin IN mas, paramout OUT mas);
END;
[/php]
Дополнительно объявим тип "mas" и процедуру "getmas()", они нам понадобятся для работы с ассоциативными массивами (Associative Arrays – Index-By Tables).
Далее в package body реализуем объявленное –
[php]
create or replace
PACKAGE BODY TEST AS
PROCEDURE getvar(paramin IN testvar, paramout OUT nocopy testvar) AS
BEGIN
paramout := testvar('1', '2', '3', '4', '5');
FOR i IN paramin.FIRST .. paramin.LAST
LOOP
paramout(i) := paramin(i) || paramout(i);
END LOOP;
END getvar;

PROCEDURE gettab(paramin IN testtab, paramout OUT nocopy testtab) AS
BEGIN
paramout := testtab('a', 'b', 'c', 'd', 'e');
FOR i IN paramin.FIRST .. paramin.LAST
LOOP
paramout(i) := paramin(i) || paramout(i);
END LOOP;
END gettab;

PROCEDURE getobj(paramin IN testobj, paramout OUT testobj) AS
x varchar2(50);
BEGIN
x:=testobj(paramin.test_id).getnum('World!');
paramout := testobj(x);
END getobj;

PROCEDURE getmas(paramin IN mas, paramout OUT mas) AS
BEGIN
paramout(1) := paramin(1) || '1';
paramout(2) := paramin(2) || '2';
paramout(3) := paramin(3) || '3';
paramout(4) := paramin(4) || '4';
paramout(5) := paramin(5) || '5';
END getmas;

END TEST;
[/php]
Внутри процедур созданы простейшие обработчики. Для наглядности результата входящим параметрам добавляем по символу и отсылаем обратно, исключение составляет объект, в котором также продемонстрирован вызов метода.
Теперь возвращаемся к asp.net.
a) Varrays.
В Server Explorer подключаемся к бд, в списке объектов базы данных находим User-Defined Types, открываем его. Видим там созданный нами тип – "testvar". Правой кнопкой мыши кликаем на нём и выбираем Generate Custom Class. В появившемся окне ничего не изменяем, доходим до Finish, кликаем на нём и далее видим новый сгенерированный Testvar.cs -
[php]
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:2.0.50727.1433
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace testorcl {
using System;
using Oracle.DataAccess.Client;
using Oracle.DataAccess.Types;
using System.Xml.Serialization;
using System.Xml.Schema;


public class TESTVAR : INullable, IOracleCustomType, IXmlSerializable {

private bool m_IsNull;

private string[] m_TESTVAR;

private OracleUdtStatus[] m_statusArray;

public TESTVAR() {
// TODO : Add code to initialise the object
}

public TESTVAR(string str) {
// TODO : Add code to initialise the object based on the given string
}

public virtual bool IsNull {
get {
return this.m_IsNull;
}
}

public static TESTVAR Null {
get {
TESTVAR obj = new TESTVAR();
obj.m_IsNull = true;
return obj;
}
}

[OracleArrayMappingAttribute()]
public virtual string[] Value {
get {
return this.m_TESTVAR;
}
set {
this.m_TESTVAR = value;
}
}

public virtual OracleUdtStatus[] StatusArray {
get {
return this.m_statusArray;
}
set {
this.m_statusArray = value;
}
}

public virtual void FromCustomObject(Oracle.DataAccess.Client.OracleConnection con, System.IntPtr pUdt) {
object objectStatusArray = ((object)(m_statusArray));
OracleUdt.SetValue(con, pUdt, 0, this.m_TESTVAR, objectStatusArray);
}

public virtual void ToCustomObject(Oracle.DataAccess.Client.OracleConnection con, System.IntPtr pUdt) {
object objectStatusArray = null;
this.m_TESTVAR = ((string[])(OracleUdt.GetValue(con, pUdt, 0, out objectStatusArray)));
this.m_statusArray = ((OracleUdtStatus[])(objectStatusArray));
}

public virtual void ReadXml(System.Xml.XmlReader reader) {
// TODO : Read Serialized Xml Data
}

public virtual void WriteXml(System.Xml.XmlWriter writer) {
// TODO : Serialize object to xml data
}

public virtual XmlSchema GetSchema() {
// TODO : Implement GetSchema
return null;
}

public override string ToString() {
// TODO : Return a string that represents the current object
return "";
}

public static TESTVAR Parse(string str) {
// TODO : Add code needed to parse the string and get the object represented by the string
return new TESTVAR();
}
}

// Factory to create an object for the above class
[OracleCustomTypeMappingAttribute("SHOP_USER.TESTVAR")]
public class TESTVARFactory : IOracleCustomTypeFactory, IOracleArrayTypeFactory {

public virtual IOracleCustomType CreateObject() {
TESTVAR obj = new TESTVAR();
return obj;
}

public virtual System.Array CreateArray(int length) {
String[] collElem = new String[length];
return collElem;
}

public virtual System.Array CreateStatusArray(int length) {
OracleUdtStatus[] udtStatus = new OracleUdtStatus[length];
return udtStatus;
}
}
}
[/php]
Теперь займёмся отправлением/получением данных -
[php]
<%
testorcl.TESTVAR outparam = new testorcl.TESTVAR();
outparam.Value = new string[] {"a", "b", "c", "d", "e"};

OracleConnection conn = new OracleConnection();
conn.ConnectionString = WebConfigurationManager.ConnectionStrings["oracle_conn"].ConnectionString;
OracleCommand cm = new OracleCommand("test.getvar", conn);
cm.CommandType = CommandType.StoredProcedure;
OracleParameter outid = new OracleParameter();
outid.OracleDbType = OracleDbType.Array;
outid.UdtTypeName = "SHOP_USER.TESTVAR";
outid.Direction = ParameterDirection.Input;
outid.Size = 50;
outid.Value = outparam;
outid.ParameterName = "outid";
cm.Parameters.Add(outid);

OracleParameter id = new OracleParameter();
id.OracleDbType = OracleDbType.Array;
id.UdtTypeName = "SHOP_USER.TESTVAR";
id.Direction = ParameterDirection.Output;
id.Size = 50;
id.ParameterName = "id";
cm.Parameters.Add(id);

conn.Open();

cm.ExecuteNonQuery();

for (int i = 0; i < (((testorcl.TESTVAR)(cm.Parameters["id"].Value)).Value).Length; i++)
{
Response.Write((((testorcl.TESTVAR)(cm.Parameters["id"].Value)).Value)[i] + "<br>");
}

cm.Dispose();
conn.Close();
%>
[/php]
б) Nested tables.
Здесь всё по аналогии с массивами.
Создаём Testtab.cs–
[php]
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:2.0.50727.1433
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace testorcl {
using System;
using Oracle.DataAccess.Client;
using Oracle.DataAccess.Types;
using System.Xml.Serialization;
using System.Xml.Schema;


public class TESTTAB : INullable, IOracleCustomType, IXmlSerializable {

private bool m_IsNull;

private string[] m_TESTTAB;

private OracleUdtStatus[] m_statusArray;

public TESTTAB() {
// TODO : Add code to initialise the object
}

public TESTTAB(string str) {
// TODO : Add code to initialise the object based on the given string
}

public virtual bool IsNull {
get {
return this.m_IsNull;
}
}

public static TESTTAB Null {
get {
TESTTAB obj = new TESTTAB();
obj.m_IsNull = true;
return obj;
}
}

[OracleArrayMappingAttribute()]
public virtual string[] Value {
get {
return this.m_TESTTAB;
}
set {
this.m_TESTTAB = value;
}
}

public virtual OracleUdtStatus[] StatusArray {
get {
return this.m_statusArray;
}
set {
this.m_statusArray = value;
}
}

public virtual void FromCustomObject(Oracle.DataAccess.Client.OracleConnection con, System.IntPtr pUdt) {
object objectStatusArray = ((object)(m_statusArray));
OracleUdt.SetValue(con, pUdt, 0, this.m_TESTTAB, objectStatusArray);
}

public virtual void ToCustomObject(Oracle.DataAccess.Client.OracleConnection con, System.IntPtr pUdt) {
object objectStatusArray = null;
this.m_TESTTAB = ((string[])(OracleUdt.GetValue(con, pUdt, 0, out objectStatusArray)));
this.m_statusArray = ((OracleUdtStatus[])(objectStatusArray));
}

public virtual void ReadXml(System.Xml.XmlReader reader) {
// TODO : Read Serialized Xml Data
}

public virtual void WriteXml(System.Xml.XmlWriter writer) {
// TODO : Serialize object to xml data
}

public virtual XmlSchema GetSchema() {
// TODO : Implement GetSchema
return null;
}

public override string ToString() {
// TODO : Return a string that represents the current object
return "";
}

public static TESTTAB Parse(string str) {
// TODO : Add code needed to parse the string and get the object represented by the string
return new TESTTAB();
}
}

// Factory to create an object for the above class
[OracleCustomTypeMappingAttribute("SHOP_USER.TESTTAB")]
public class TESTTABFactory : IOracleCustomTypeFactory, IOracleArrayTypeFactory {

public virtual IOracleCustomType CreateObject() {
TESTTAB obj = new TESTTAB();
return obj;
}

public virtual System.Array CreateArray(int length) {
String[] collElem = new String[length];
return collElem;
}

public virtual System.Array CreateStatusArray(int length) {
OracleUdtStatus[] udtStatus = new OracleUdtStatus[length];
return udtStatus;
}
}
}
[/php]
И отправляем/получаем данные –
[php]
<%
testorcl.TESTTAB outpar = new testorcl.TESTTAB();
outpar.Value = new string[] { "1", "2", "3", "4", "5" };

OracleConnection con = new OracleConnection();
con.ConnectionString = WebConfigurationManager.ConnectionStrings["oracle_conn"].ConnectionString;
OracleCommand cmd = new OracleCommand("test.gettab", con);
cmd.CommandType = CommandType.StoredProcedure;
OracleParameter parid = new OracleParameter();
parid.OracleDbType = OracleDbType.Array;
parid.UdtTypeName = "SHOP_USER.TESTTAB";
parid.Direction = ParameterDirection.Input;
parid.Size = 50;
parid.Value = outpar;
parid.ParameterName = "parid";
cmd.Parameters.Add(parid);

OracleParameter param = new OracleParameter();
param.OracleDbType = OracleDbType.Array;
param.UdtTypeName = "SHOP_USER.TESTTAB";
param.Direction = ParameterDirection.Output;
param.Size = 100;
param.ParameterName = "param";
cmd.Parameters.Add(param);

con.Open();

cmd.ExecuteNonQuery();
for (int i = 0; i < (((testorcl.TESTTAB)(cmd.Parameters["param"].Value)).Value).Length; i++)
{
Response.Write((((testorcl.TESTTAB)(cmd.Parameters["param"].Value)).Value)[i] + "<br>");
}
cmd.Dispose();
con.Close();
%>
[/php]
0
Вернуться в начало страницы
 
Ответить с цитированием данного сообщения
Alcorn
Alcorn
Topic Starter сообщение 2.9.2010, 7:11; Ответить: Alcorn
Сообщение #2


в) Objects.
Тут тоже, принцип создания по аналогии с предыдущими, Testobj.cs -
[php]
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:2.0.50727.1433
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace testorcl {
using System;
using Oracle.DataAccess.Client;
using Oracle.DataAccess.Types;
using System.Xml.Serialization;
using System.Xml.Schema;


public class TESTOBJ : INullable, IOracleCustomType, IXmlSerializable {

private bool m_IsNull;

private string m_TEST_ID;

public TESTOBJ() {
// TODO : Add code to initialise the object
}

public TESTOBJ(string str) {
// TODO : Add code to initialise the object based on the given string
}

public virtual bool IsNull {
get {
return this.m_IsNull;
}
}

public static TESTOBJ Null {
get {
TESTOBJ obj = new TESTOBJ();
obj.m_IsNull = true;
return obj;
}
}

[OracleObjectMappingAttribute("TEST_ID")]
public string TEST_ID {
get {
return this.m_TEST_ID;
}
set {
this.m_TEST_ID = value;
}
}

public virtual void FromCustomObject(Oracle.DataAccess.Client.OracleConnection con, System.IntPtr pUdt) {
Oracle.DataAccess.Types.OracleUdt.SetValue(con, pUdt, "TEST_ID", this.TEST_ID);
}

public virtual void ToCustomObject(Oracle.DataAccess.Client.OracleConnection con, System.IntPtr pUdt) {
this.TEST_ID = ((string)(Oracle.DataAccess.Types.OracleUdt.GetValue(con, pUdt, "TEST_ID")));
}

public virtual void ReadXml(System.Xml.XmlReader reader) {
// TODO : Read Serialized Xml Data
}

public virtual void WriteXml(System.Xml.XmlWriter writer) {
// TODO : Serialize object to xml data
}

public virtual XmlSchema GetSchema() {
// TODO : Implement GetSchema
return null;
}

public override string ToString() {
// TODO : Return a string that represents the current object
return "";
}

public static TESTOBJ Parse(string str) {
// TODO : Add code needed to parse the string and get the object represented by the string
return new TESTOBJ();
}
}

// Factory to create an object for the above class
[OracleCustomTypeMappingAttribute("SHOP_USER.TESTOBJ")]
public class TESTOBJFactory : IOracleCustomTypeFactory {

public virtual IOracleCustomType CreateObject() {
TESTOBJ obj = new TESTOBJ();
return obj;
}
}
}
[/php]
Отправка/получение данных –
[php]
<%
testorcl.TESTOBJ outobj = new testorcl.TESTOBJ();
outobj.TEST_ID = "Hello";

OracleConnection cons = new OracleConnection();
cons.ConnectionString = WebConfigurationManager.ConnectionStrings["oracle_conn"].ConnectionString;
OracleCommand cmds = new OracleCommand("test.getobj", cons);
cmds.CommandType = CommandType.StoredProcedure;
OracleParameter parobj = new OracleParameter();
parobj.OracleDbType = OracleDbType.Object;
parobj.UdtTypeName = "SHOP_USER.TESTOBJ";
parobj.Direction = ParameterDirection.Input;
parobj.Size = 100;
parobj.Value = outobj;
parobj.ParameterName = "parobj";
cmds.Parameters.Add(parobj);

OracleParameter par = new OracleParameter();
par.OracleDbType = OracleDbType.Object;
par.UdtTypeName = "SHOP_USER.TESTOBJ";
par.Direction = ParameterDirection.Output;
par.Size = 50;
par.ParameterName = "par";
cmds.Parameters.Add(par);

cons.Open();

cmds.ExecuteNonQuery();
Response.Write(((testorcl.TESTOBJ)(cmds.Parameters["par"].Value)).TEST_ID);
cmds.Dispose();
cons.Close();
%>
[/php]
где TEST_ID – атрибут объекта.

Associative Arrays (ассоциативные массивы).

Этот тип данных не создаётся в Oracle types, а объявляется прямо в package (в данном примере), поэтому его обсуждение вынесено отдельно. Отличий от предыдущих вариантов довольно много - для данных плавающего размера, например varchar2, перед вызовом обязательно должен быть задан их размер, а также размер каждого элемента массива и общее их количество в каждом параметре, добавились новые параметры – ArrayBindStatus и ArrayBindSize.
В отличие от предыдущих примеров, в Visual Studio никаких дополнительных UDT создавать не требуется.
В этом примере отсылаем данные (ассоциативный массив) в процедуру, там их обрабатываем и возвращаем обратно. Сама pl/sql процедура приведена выше, а здесь код на c# -
[php]
<%
OracleConnection cont = new OracleConnection();
cont.ConnectionString = WebConfigurationManager.ConnectionStrings["oracle_conn"].ConnectionString;
OracleCommand cmdt = new OracleCommand("test.getmas", cont);
cmdt.CommandType = CommandType.StoredProcedure;

OracleParameter parmas = new OracleParameter();
parmas.CollectionType = OracleCollectionType.PLSQLAssociativeArray;
parmas.OracleDbType = OracleDbType.Varchar2;
parmas.Direction = ParameterDirection.Input;
parmas.Size = 5;
parmas.Value = new string[5] {"a", "b", "c", "d", "e"};
parmas.ArrayBindSize = new int[5] {1, 1, 1, 1, 1};
parmas.ParameterName = "parmas";
parmas.ArrayBindStatus = new OracleParameterStatus[5] {
OracleParameterStatus.Success, OracleParameterStatus.Success,
OracleParameterStatus.Success, OracleParameterStatus.Success,
OracleParameterStatus.Success};
cmdt.Parameters.Add(parmas);
OracleParameter parmasout = new OracleParameter();
parmasout.CollectionType = OracleCollectionType.PLSQLAssociativeArray;
parmasout.OracleDbType = OracleDbType.Varchar2;
parmasout.Direction = ParameterDirection.Output;
parmasout.Size = 5;
parmasout.ArrayBindSize = new int[5] {50, 50, 50, 50, 50};
parmasout.ParameterName = "parmasout";
cmdt.Parameters.Add(parmasout);

cont.Open();

cmdt.ExecuteNonQuery();
for (int i = 0; i < ((Array)(cmdt.Parameters["parmasout"].Value)).Length; i++)
{
Response.Write(((Array)(cmdt.Parameters["parmasout"].Value)).GetValue(i) + "<br>");
}
cmdt.Dispose();
cont.Close();
%>
[/php]
где в output-параметре parmasout.Size = 5; - количество элементов массива, ArrayBindSize 50 – максимальный размер каждого элемента (берётся из объявления типа в pl/sql процедуре).

Порядок параметров.

Во всех вышеприведённых примерах порядок объявления параметров должен строго совпадать с указанием этих параметров в pl/sql процедуре.
В качестве аналога можно привести функцию – func(x,y). При её вызове мы передаём параметры как x,y, а не наоборот, как y,x. Тоже самое происходит и здесь, первый по счёту параметр в c# будет первым по счёту параметром и в pl/sql процедуре. При перемешивании параметров мы естественно получим ошибку.
Но если мы хотим обойти это ограничение, то тут нам поможет связывание параметров по именам, тогда очередность их объявления в c# будет не важна.
В этом случае основное правило – имя параметра в с# должно совпадать с именем параметра в вызываемой pl/sql процедуре.
Имя параметра задаётся при его создании –
[php]
param.ParameterName = "paramout";
[/php]
Далее после создания объекта OracleCommand добавим связывание по имени –
[php]
cmd.BindByName = true;
[/php]
Теперь можем располагать параметры в любом удобном для нас порядке.
Всё тестировалось в Oracle 11g, .NET Framework 3.5, Visual Studio 2008, Windows 2003 Server Enterprise Edition SP2.
© Автор Alcorn.

Замечание модератора:
Эта тема была закрыта автоматически ввиду отсутствия активности в ней на протяжении 100+ дней.
Если Вы считаете ее актуальной и хотите оставить сообщение, то воспользуйтесь кнопкой
или обратитесь к любому из модераторов.
Вернуться в начало страницы
 
Ответить с цитированием данного сообщения
Открыть тему
Тема закрыта
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0


Свернуть

> Похожие темы

  Тема Ответов Автор Просмотров Последний ответ
Горячая тема (нет новых ответов) Betatransfer.net - прием платежей для HIGH RISK проектов, интернет эквайринг и мерчант онлайн оплат
52 arendator 34149 26.3.2024, 4:43
автор: arendator
Открытая тема (нет новых ответов) HideHost.net - Виртуальные и выделенные сервера под любые Ваши проекты с индивидуальным подходом.
Работаем уже более 12 лет
6 HIDEHOST 2313 21.3.2024, 13:33
автор: Tihohodka
Горячая тема (нет новых ответов) PROXY6.NET - Индивидуальные прокси / IPv6 от 3.6 руб (29 подсеть) / IPv4 = 69.9 руб (RU/US/UA)
113 proxy6 70449 14.2.2024, 14:47
автор: proxy6
Открытая тема (нет новых ответов) GogetTop.net - сервис аренды ссылок с сети PBN адалт тьюбо
0 Crimean 905 18.12.2023, 12:20
автор: Crimean
Горячая тема (нет новых ответов) ВНИМАНИЕ: Pharmcash.net - монстр конверта Евро фарма траффика
31 Herbalka 49782 11.12.2023, 21:26
автор: Herbalka


 



RSS Текстовая версия Сейчас: 29.3.2024, 14:13
Дизайн