65.9K
CodeProject 正在变化。 阅读更多。
Home

C# 与 Oracle PL/SQL 处理

starIconstarIconstarIcon
emptyStarIcon
starIcon
emptyStarIcon

3.18/5 (34投票s)

2007 年 4 月 29 日

CPOL

4分钟阅读

viewsIcon

128929

一篇描述 Oracle PL/SQL 和 C# 之间协作的替代方法的文章

Title:       "Processing with C# and Oracle PL/SQL "BIG" transactions"
Author:      Dimitar Nikolaev Madjarov 
Email:       madjarov_d_n@yahoo.com
Language:    MS Visual Studio 2003, 2005, ASP.NET, C# and Oracle DB
Platform:    Windows, .NET 3.0 etc
Technology:  ASP.NET, GDI+
Level:       Beginner, Intermediate, Advanced
Description: An article which describes an alternative way of cooperation between Oracle PL/SQL and C#
Section      SQL, PL/SQL, Database and C# programming
SubSection   General

引言

这篇文章实际上是我关于 Oracle 和 C# 编程的第一个主题文章的继承者。第一篇文章可以在以下网址阅读 “C#、Oracle 事务和 Oracle 异常处理”。在我的第一篇文章中,我描述了一种使用强大的 Oracle 事务的替代方法。通过这种方法,我们使用原始的 PL/SQL 代码来控制事务,而不是使用 C# 代码从应用程序的源代码中管理事务。当一个接一个地使用我们的 Oracle 存储过程时,这是有效的。在这种情况下,每个过程都使用存储过程体内的本地提交和回滚/事务控制。对于简单的应用程序来说,这很完美,但不幸的是,现实生活要复杂一些。在实际的业务逻辑中,我们有许多不同的执行存储过程的组合,它们以复杂的方式执行/许多时候这种顺序取决于先前存储过程的执行状态/当它们都执行了它们的 PL/SQL 代码后,我们就必须提交所有更改,或者当我们的执行列表顺序中有一个存储过程未能执行其 PL/SQL 时,我们必须回滚所有更改并触发一个带有关于结果的合适消息的事件,是我们的源代码失败还是成功完成了它的任务。

使用的 PL/SQL

首先,我们需要创建我们的演示表和演示包。以下是我们将在演示中使用到的所有 PL/SQL 代码。在本节中,我们将创建我们的表。请注意,我将字段 'CREATEDATE' 的数据类型创建为 'DATE'。我这样做是有意为之,主要目的是向您展示一个关于如何在 Oracle 和 C# 之间处理/交换 'DATE' 类型数据的小技巧,当您在 C# 代码中构建 PL/SQL 时。

CREATE TABLE T1
  (
    ID         NUMBER   (4) NOT NULL,
    NAME       VARCHAR2 (100),
    CREATEDATE DATE DEFAULT SYSDATE NOT NULL
 )
/

ALTER TABLE T1 ADD CONSTRAINT PK_T1_ID
  PRIMARY KEY (ID)
/



CREATE TABLE T2
  (
   ID   NUMBER   (4) NOT NULL,
   NAME VARCHAR2 (100)
 )
/

ALTER TABLE T2 ADD CONSTRAINT PK_T2_ID
  PRIMARY KEY (ID)
/

在本节中,我们将创建我们的插入存储过程

CREATE OR REPLACE PACKAGE PACKAGEDEMO IS

PROCEDURE T1ADD(ID   in number,  
                Name in varchar2, 
                CRDATE DATE,
                error_sql_value out varchar2,
                DoTrans in number default 1);
PROCEDURE T2ADD(ID   in number,  
                Name in varchar2, 
                error_sql_value out varchar2,
                DoTrans in number default 1);

END PACKAGEDEMO;
/

create or replace package body PACKAGEDEMO is

PROCEDURE T1ADD(ID in number, Name in varchar2, CRDATE DATE, 
                error_sql_value out varchar2, DoTrans in number default 1)
AS BEGIN
    error_sql_value   := '-1';
    INSERT INTO T1 (ID, NAME, CREATEDATE) VALUES (ID, Name, CRDATE);
    IF (DoTrans = 1) THEN
         COMMIT;
        END IF;
    EXCEPTION
    WHEN OTHERS THEN BEGIN
      IF (DoTrans = 1) THEN
       ROLLBACK;
      END IF;
     error_sql_value := 'STOREP ROCEDURE "T1ADD" in PACKAGE "PACKAGEDEMO" ERROR: '||SUBSTR(SQLERRM, 1, 255)||', ERROR CODE: '||SQLCODE;
    END;
END;


PROCEDURE T2ADD(ID in number, Name in varchar2, 
                error_sql_value out varchar2, DoTrans in number default 1)
AS BEGIN
    error_sql_value   := '-1';
    INSERT INTO T2 (ID, NAME) VALUES (ID, Name);
     IF (DoTrans = 1) THEN
           COMMIT;
         END IF;
    EXCEPTION
    WHEN OTHERS THEN BEGIN
      IF (DoTrans = 1) THEN
        ROLLBACK;
      END IF;
     error_sql_value := 'STOREP ROCEDURE "T2ADD" in PACKAGE "PACKAGEDEMO" ERROR: '||SUBSTR(SQLERRM, 1, 255)||', ERROR CODE: '||SQLCODE;
    END;
END;
end PACKAGEDEMO;
/     

PL/SQL 术语中的主要思想

现在我们将简要描述我的 PL/SQL 控制 Oracle 事务的思想是如何工作的

[I]。在第一点中,我们将使用 C# 的 'ExecuteNonQuery' 代码执行一个单独的存储过程作为 PL/SQL。当然,您也可以使用其他 C# 方法来执行存储过程,但我将使用 'ExecuteNonQuery' ADO.NET 方法,因为在下一节中,当我们必须构建一个复杂的业务逻辑时,我将确切地使用这种方法。(ExecuteNonQuery)

The deal here is to build the Pl/SQL above into your C# code and to 
execute it as using 'ExecuteNonQuery' method. Into a try/catch passage 
you have to catch the error and manipulate it. This example is how to 
use a simple transaction. Executed store procedure is alone and 
transactions management as Commit/Rollback statements are ruled 
into body of our stored procedure 't2add'.

DECLARE
error_sql_value varchar2 (4000);  
BEGIN
PACKAGEDEMO.t2add(10,'Test_10',error_sql_value);
IF (error_sql_value <> '-1') THEN
 RAISE_APPLICATION_ERROR (-20000, error_sql_value);
END IF;
 EXCEPTION
    WHEN OTHERS THEN BEGIN
    ROLLBACK;
    RAISE_APPLICATION_ERROR (-20000, error_sql_value);
 END;
COMMIT;
END;       

[II]。在第二点中,我们将执行一个包含两个不同存储过程的复杂方案。我们将使用相同的 ADO.NET 方法 'ExecuteNonQuery',但现在我们无法控制每个存储过程中的事务,因为逻辑是如果第一个过程失败,我们必须回滚所有更改。同样,如果第一个过程通过但第二个过程失败,我们也必须回滚所有更改。我们必须在两个过程都通过的情况下设置提交更改。

The deal here is to build the Pl/SQL above into your C# code and to execute it as using 'ExecuteNonQuery' method. 

Into a try/catch passage you have to catch the error and manipulate it. 
Please notice that now parameter 'DoTrans" has value '0' and by this way we send a command to our store procedures
to stop manage transactions statements locally (inside the procedure body).

 Now we manage transactions as Pl/SQL in the SQL statement which we build inside of our C# code.

DECLARE
error_sql_value varchar2(4000);  
BEGIN
SAVEPOINT LOCALSP1;

PACKAGEDEMO.t1add(0,'Test_0', TO_DATE('17.04.2007 16:04:12','DD.MM.YYYY HH24:MI:SS'), error_sql_value,0);
IF (error_sql_value <> '-1') THEN
 RAISE_APPLICATION_ERROR (-20000, error_sql_value);
END IF;

PACKAGEDEMO.t2add(0,'Test_0',error_sql_value,0);
IF (error_sql_value <> '-1') THEN
 RAISE_APPLICATION_ERROR (-20000, error_sql_value);
END IF;

PACKAGEDEMO.t2add(1,'Test_1',error_sql_value,0);
IF (error_sql_value <> '-1') THEN
 RAISE_APPLICATION_ERROR (-20000, error_sql_value);
END IF;

 EXCEPTION
    WHEN OTHERS THEN BEGIN
    ROLLBACK TO LOCALSP1;
    RAISE_APPLICATION_ERROR (-20000, error_sql_value);
 END;
COMMIT;
END;       

C# 代码

到目前为止,我们已经完成了这篇文章中最有价值的一步。我希望您能理解上面的主要思想。在本节中,我将做更简单的任务,使用 C# 代码构建我们第 II 点的 PL/SQL,并使用 ADO.NET 方法 'ExecuteNonQuery' 执行该 PL/SQL。您可以使用我上一篇文章中的 Oracle 方法来访问 Oracle 数据库并从中管理数据。

using System;
using System.Data;
using System.Configuration;
using System.Text;
using System.IO;

namespace ORacle.Logic
{
        public class ORATransactions 
    {   
               public ORATransactions()
               {
                 //
                 // An empty method
                 //
               }
               
                
        private string OraString(string inputStr)
        {
            if (inputStr == null)
            {
                return "NULL";
            }
                        
              //
             // Replacing oracle special characters
             //
            inputStr = inputStr.Replace("'", "''");
                        
            //
            // Adding ' to string
            //
            inputStr = string.Format("'{0}'", inputStr);
                        
            return inputStr;
        }
                

                
        private string OraDateTime(DateTime date)
        {
                return date.ToString("dd.MM.yyyy HH:MM:ss");
        }
                
                
        public string UpdateOraSQL(ourTestData data) 
        {
            string ExecuteSQL  = String.Empty;
            
            ExecuteSQL  = "DECLARE \n";
            ExecuteSQL += "error_sql_value varchar2(4000); \n";
            ExecuteSQL += "BEGIN \n";
            ExecuteSQL += "SAVEPOINT LOCALSP1; \n";  
                        
                        //
            // Bind PACKAGEDEMO.t1add store procedure
            //
            ExecuteSQL += String.Format("PACKAGEDEMO.t1add({0}, {1}, TO_DATE('{2}','DD.MM.YYYY HH24:MI:SS'), error_sql_value, 0); \n",
                                     data.ID[0].ToString(), OraString(data.NAME[0].ToString()), OraDateTime(data.CRDATE[0].CREATED)
                                    );
            ExecuteSQL += "IF (error_sql_value <> '-1') THEN \n";
            ExecuteSQL += "RAISE_APPLICATION_ERROR (-20000, error_sql_value||' '||error_sql_descr); \n";
            ExecuteSQL += "END IF; \n";
                        
            //
            // Bind PACKAGEDEMO.t2add store procedure
            //
            ExecuteSQL     += String.Format("PACKAGEDEMO.t2add({0},{1},error_sql_value,0); \n",
                                        data.ID[1].ToString(), OraString(data.Name[1].ToString())
                                                        );
            ExecuteSQL     += "IF (error_sql_value <> '-1') THEN \n";
            ExecuteSQL     += "RAISE_APPLICATION_ERROR (-20000, error_sql_value||' '||error_sql_descr); \n";
            ExecuteSQL     += "END IF; \n";                      
                                               
                        ExecuteSQL += "EXCEPTION \n";
            ExecuteSQL += "WHEN OTHERS THEN BEGIN \n";
            ExecuteSQL += "ROLLBACK TO LOCALSP1; \n";
            ExecuteSQL += "RAISE_APPLICATION_ERROR (-20000, error_sql_descr||' '||error_sql_value); \n";
            ExecuteSQL += "END; \n";
            ExecuteSQL += "COMMIT; \n";
            ExecuteSQL += "END; \n";

            return ExecuteSQL;
         }
                
                
         public string UpdateOra(ourTestData data) 
         {

            string ReturnValue  = "-1";
                   OracleBuisinessDalc dalc        = new OracleBuisinessDalc();
            
            try
            {
                     ReturnValue = dalc.Execute_Non_Query(UpdateOraSQL(data));
            }
            catch (Exception ex)
            {
                ReturnValue = ex.Message;
            }
            finally
            {
                    return ReturnValue;
            }        
        }
        }
}



Finally you my execute the method string UpdateOra(ourTestData data) into your 'Page-Load' method for an example and you will 
receive '-1' if all is Ok or the full error description in case that something was wrong. 
Of course this is also a simple example but by this way you may compose very complicate business logic and finally to generate a 'big' string
which contain the PL/SQL similar as this from point.II. 

After that you may execute this string from your C#, VB.NET code and receive the status of executing SQL.

结论

在本文中,我试图解释一种稍微不同的处理 Oracle 事务和直接在存储过程中捕获 Oracle 异常的方法,并在不冒任何风险的情况下处理它们,以免影响与存储在 Oracle 中的数据交互的 ASP.NET/C# 应用程序。我想说的是,这只是一种与 Oracle 和 C# 交互的不同方式,也许不是完美的方式,但本文的作者知道,在生活中,拥有选择的自由是一件大事。我希望您能评价这篇文章,以及它向您描述的通过另一种方式处理 Oracle 事务的选项将对您和您的业务有用。

© . All rights reserved.