2012-04-18 13 views
8

This answer do this question jest w porządku, ale szukam kodu ADO.NET, aby móc wysłać tablicę lub tabelę do procedury Oracle, a następnie użyć tej tabeli w procedurze.Jak korzystać z Array/Table Parameter do Oracle (ODP.NET 10g) przez ADO.NET/C#?

W parametrów wycenione tabela SQL Server, to całkiem proste:

CREATE TYPE [dbo].[IntTable] AS TABLE(
    [intvalue] [int] NOT NULL, 
    PRIMARY KEY CLUSTERED 
    (
     [intvalue] ASC 
    )WITH (IGNORE_DUP_KEY = OFF) 
) 
GO 

CREATE PROCEDURE dbo.UseTable 
    @SomeInt INT 
    ,@IntTable dbo.IntTable READONLY 
AS 
BEGIN 
    -- Do whatever using @SomeInt and @IntTable like: 
    INSERT INTO Assignments (masterid, childid) 
    SELECT @SomeInt, intvalue 
    FROM @IntTable 
END 
GO 

Następnie na kliencie:

var param = new List<int>(); 
param.Add(1); 
param.Add(2); 

Cm.Parameters 
    .AddWithValue("@IntTable", param /* IEnumerable<Int> */) 
    .SqlDbType = SqlDbType.Structured 

To co obecnie mam:

CREATE OR REPLACE TYPE TRAIT_ID_TABLE AS TABLE OF NUMBER; 

PROCEDURE SET_TRAITS(P_CUST_TANK_PROD_ID IN CUST_TANK_PROD.CUST_TANK_PROD_ID%TYPE, P_TRAIT_IDS IN TRAIT_ID_TABLE) 
AS 
BEGIN 
    DELETE FROM TANK_TRAIT 
     WHERE CUST_TANK_PROD_ID = P_CUST_TANK_PROD_ID; 

    INSERT INTO TANK_TRAIT(CUST_TANK_PROD_ID, TRAIT_ID) 
    SELECT P_CUST_TANK_PROD_ID, COLUMN_VALUE FROM TABLE(P_TRAIT_IDS); 

    COMMIT; 
EXCEPTION 
    WHEN OTHERS 
    THEN 
    ROLLBACK; 
END; 


var param = new OracleParameter(); 
param.ParameterName = "P_TRAIT_IDS"; 
param.OracleDbType = OracleDbType.Decimal; 
param.CollectionType = OracleCollectionType.PLSQLAssociativeArray; 
param.Direction = ParameterDirection.Input; 
param.Value = traitIdList.ToArray<int>(); 
param.Size = traitIdList.Count; 
cmd.Parameters.Add(param); 

I Otrzymuję to na ExecuteNonQuery:

System.AccessViolationException was caught 
    Message=Attempted to read or write protected memory. This is often an indication that other memory is corrupt. 
    Source=Oracle.DataAccess 
    StackTrace: 
     at Oracle.DataAccess.Client.OpsSql.ExecuteNonQuery(IntPtr opsConCtx, IntPtr& opsErrCtx, IntPtr& opsSqlCtx, IntPtr& opsDacCtx, IntPtr opsSubscrCtx, Int32& isSubscrRegistered, OpoSqlValCtx*& pOpoSqlValCtx, OpoSqlRefCtx& pOpoSqlRefCtx, IntPtr[] pOpoPrmValCtx, OpoPrmRefCtx[] pOpoPrmRefCtx, OpoMetValCtx*& pOpoMetValCtx, Int32 prmCnt) 
     at Oracle.DataAccess.Client.OracleCommand.ExecuteNonQuery() 
     at EDC2.Domain.TraitList.SaveTraits(String connectionString) in C:\code\EDC2\trunk\app\EDC2.Domain\Trait.cs:line 195 
    InnerException: 
+1

proszę zobaczyć http://stackoverflow.com/questions/5557318/can-an-oracle-stored-procedure-that-has-a-nested-table-parameter-be-called-- od – Eggi

+0

@Eggi Niestety ta technika nie działa z 10g clie nt i próba połączenia klienta 11g i klienta 10g działają w .NET (lub nawet sprawiają, że starają się uruchomić wszystkie na kliencie 11g) daje mi pasowania z niekompatybilnymi błędami dostawcy i problemami z inicjalizacją konstruktorów. –

Odpowiedz

18

Działa to dla ODP.NET (ODAC):

Pakiet Oracle będzie konfiguracja jak:

CREATE OR REPLACE package SOME_PACKAGE as 
    ... 
    type t_number_tab is table of number index by pls_integer; 
    ... 
    procedure ins_test(i_id_tab in t_number_tab, o_inserted out number); 

end SOME_PACKAGE; 


CREATE OR REPLACE package body SOME_PACKAGE as 

    procedure ins_test(i_id_tab in t_number_tab, o_inserted out number) is 
    begin 
     -- inserts all records to test table based on incoming table of ids 
     forall i in i_id_tab.first .. i_id_tab.last 
      insert into TEST_TAB 
      (id, val1, val2) 
      select id,val1,val2 
      from main_tab 
      where id = i_id_tab(i); 

     o_inserted := SQL%ROWCOUNT; 

     commit; 
    exception 
     when others then 
      rollback; 
      raise; 
    end ins_test; 
... 
end SOME_PACKAGE; 

Następnie Twój kod C# wyglądałby następująco:

string connStr = "User Id=xxx;Password=xxxx;Data Source=xxxxx;"; 
OracleConnection _conn = new OracleConnection(connStr); 
_conn.Open(); 

OracleCommand cmd = _conn.CreateCommand(); 
cmd.CommandText = "some_package.ins_test"; 
cmd.CommandType = CommandType.StoredProcedure; 

OracleParameter p1 = new OracleParameter(); 
OracleParameter p2 = new OracleParameter(); 

p1.OracleDbType = OracleDbType.Decimal; 
p1.Direction = ParameterDirection.Input; 
p2.OracleDbType = OracleDbType.Decimal; 
p2.Direction = ParameterDirection.Output; 

p1.CollectionType = OracleCollectionType.PLSQLAssociativeArray; 
p1.Value = new int[3] { 1, 2, 3 }; 
p1.Size = 3; 

cmd.Parameters.Add(p1); 
cmd.Parameters.Add(p2); 

cmd.ExecuteNonQuery(); 
+0

Dzięki - Próbuję tego zamiast metody UDT, która działa tylko z 11g klienta w rozwoju. Jednak otrzymuję System.AccessViolationException –

+0

wow, czy używasz niezarządzanego kodu? – tbone

+0

nie, nie zarządzany kod –

Powiązane problemy