OracleCommand与OracleDependency永远等待

时间:2014-09-02 17:56:14

标签: c# .net oracle odp.net

注意: here上的相关问题没有解决方案

请记住,我不是Oracle的专家或针对Oracle的编程。这是我的测试环境。我在架构STVM中有一个名为STVM_NOTIFICATION的表。这就是它的样子:

CREATE TABLE STVM_NOTIFICATION
(   
    "ID"              NUMBER              NOT NULL, 
    "PROPERTYNAME"    VARCHAR2(16 BYTE)   NOT NULL, 
    "PROPERTYVALUE"   VARCHAR2(16 BYTE)   NOT NULL, 
    "ACTION"          VARCHAR2(32 BYTE)   NOT NULL, 
    "POSTDATE"        TIMESTAMP (6)       NOT NULL, 
    "SENT"            CHAR(1 BYTE)        NOT NULL,
    ADD CONSTRAINT "PK_ID" PRIMARY KEY ("ID")
)

我创建了以下序列并触发为每行创建唯一标识:

CREATE SEQUENCE STVM_NOTIF_SEQ
  START WITH 1
  INCREMENT BY 1
  CACHE 100;

CREATE OR REPLACE TRIGGER STVM_NOTIF_ID_TRG BEFORE INSERT ON STVM_NOTIFICATION
  FOR EACH ROW
    BEGIN
      :NEW.ID := STVM_NOTIF_SEQ.NEXTVAL;
    END;

然后我为STVM设置了以下授权:

GRANT CREATE SESSION TO STVM;
GRANT CREATE TABLE TO STVM;
GRANT CREATE VIEW TO STVM;
GRANT CREATE ANY TRIGGER TO STVM;
GRANT CREATE ANY PROCEDURE TO STVM;
GRANT CREATE SEQUENCE TO STVM;
GRANT CREATE SYNONYM TO STVM;
GRANT CHANGE NOTIFICATION TO STVM;

插入表格工作正常。下面是OracleDependency上的Oracle文档提供的简单测试应用程序(经过微小修改),我用它来测试通知:

namespace SqlDependencyTest
{
    class Program
    {
        private static string oraConnectionString = @"Data Source=(DESCRIPTION=(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.0.164)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=XE)));User Id=STVM;Password=STVM;";
        private static string oraQuery = "SELECT ID FROM STVM_NOTIFICATION";
        private static OracleDependency oraDependency;

        static void Main(string[] args)
        {
            using (OracleConnection oraConnection = new OracleConnection(oraConnectionString))
            {
                try
                {
                    // Open the connection
                    oraConnection.Open();

                    // Create the Select command retrieving all data from the STVM_NOTIFICATION table. 
                    OracleCommand selectCommand = new OracleCommand(oraQuery, oraConnection);
                    // Create an OracleDependency object and set it to track the result set returned by selectCommand. 
                    oraDependency = new OracleDependency(selectCommand);

                    // Setting object-based change notification registration 
                    oraDependency.QueryBasedNotification = false;

                    // When the IsNotifiedOnce property is true, only the first change  
                    // of the traced result set will generate a notification. 
                    // Otherwise, notifications will be sent on each change  
                    // during the selectCommand.Notification.Timeout period. 
                    selectCommand.Notification.IsNotifiedOnce = true;

                    // Set the event handler to the OnChange event. 
                    oraDependency.OnChange += new OnChangeEventHandler(OnChange);

                    // When the select command is executed at the first time, a notification  
                    // on changes of the corresponding result set is registered on the server.
                    //selectCommand.CommandTimeout = 5;
                    OracleDataReader reader = selectCommand.ExecuteReader(CommandBehavior.Default);

                    // Set and execute an insert command. The Dept table data will be changed,  
                    // and a notification will be sent, causing the OnChange event of the 'dependency' object. 
                    OracleCommand insertCommand = new OracleCommand
                        ("INSERT INTO STVM_NOTIFICATION (PROPERTYNAME, PROPERTYVALUE, ACTION, POSTDATE, SENT) VALUES ('Heartbeat', 'NOK', 'REFRESH', SYSDATE, 'N')", oraConnection);
                    insertCommand.ExecuteNonQuery();

                    // Pause the current thread to process the event. 
                    Console.Read();
                }
                catch (Exception e)
                {
                    Console.WriteLine("Exception encountered: {0}", e.Message);
                }
                // Always try to both remove the notification registration
                // oraConnection.Close() is autimatically called by .Dispose at the end of our 'using' statement
                finally
                {
                    try
                    {
                        oraDependency.RemoveRegistration(oraConnection);
                    }
                    catch (Exception e)
                    {
                        Console.WriteLine("Exception encountered: {0}", e.Message);
                    }
                }
            }
        }

        // A simple event handler to handle the OnChange event. 
        // Prints the change notification details. 
        private static void OnChange(Object sender, OracleNotificationEventArgs args)
        {
            DataTable dt = args.Details;

            Console.WriteLine("The following database objects were changed:");
            foreach (string resource in args.ResourceNames)
            {
                Console.WriteLine(resource);
            }

            Console.WriteLine("\n Details:");
            Console.Write(new string('*', 80));
            for (int rows = 0; rows < dt.Rows.Count; rows++)
            {
                Console.WriteLine("Resource name: " + dt.Rows[rows].ItemArray[0]);
                string type = Enum.GetName(typeof(OracleNotificationInfo), dt.Rows[rows].ItemArray[1]);
                Console.WriteLine("Change type: " + type);
                Console.Write(new string('*', 80));
            }
        } 
    }
}

这实际上有效!但是:只有在另一个进程在同一个表上执行插入时,至少,这是我的观察。我从SQL Developer多次执行插入,问题是可以重新编写的,我已经这样做了10多次。

只要另一个进程执行了所述插入,我的应用程序就会挂起以下语句: OracleDataReader reader = selectCommand.ExecuteReader(CommandBehavior.Default);

我可以清楚地看到在 DBA_CHANGE_NOTIFICATION_REGS 中注册的通知回调: net8://(ADDRESS=(PROTOCOL=tcp)(HOST=192.168.0.226)(PORT=64268))?PR=0

连接在Oracle服务器上保持打开30分钟,直到超时为止:

SELECT USERNAME, PROGRAM, BLOCKING_SESSION_STATUS, BLOCKING_INSTANCE, BLOCKING_SESSION, EVENT FROM V$SESSION
WHERE USERNAME = 'STVM'

USERNAME    PROGRAM                         BLOCKING_SESSION_STATUS BLOCKING_INSTANCE   BLOCKING_SESSION    EVENT
STVM        SQL Developer                   NO HOLDER               (null)              (null)              SQL*Net message from client
STVM        OracleDependencyTest.vshost.exe VALID                   1                   50                  enq: TM - contention

每当发生这种情况时,唯一的解决办法是终止会话,撤销更改通知并再次授予它。在此之后,我的应用程序将再次运行,直到另一个进程执行插入。

我唯一的线索是事件 enq:TM - 争用,但大多数人将此称为表中非索引外键的指示符。考虑到我目前只有一个用于测试目的的表,在这里没有很多外键。

有没有人对 enq:TM - 争用的相关性有任何想法?

我正在使用:

(服务器端)

Oracle Database 11g Express Edition Release 11.2.0.2.0 - 64bit Production
PL/SQL Release 11.2.0.2.0 - Production
"CORE   11.2.0.2.0  Production"
TNS for Linux: Version 11.2.0.2.0 - Production
NLSRTL Version 11.2.0.2.0 - Production
Oracle.DataAccess

(客户端)

Oracle Call Interface (OCI)                                          11.2.0.1.0
Oracle Client                                                        11.2.0.1.0
Oracle Data Provider for .NET                                        11.2.0.1.0
Oracle JDBC/OCI Instant Client                                       11.2.0.1.0
Oracle JDBC/THIN Interfaces                                          11.2.0.1.0
Oracle SQL Developer                                                 11.2.0.1.0
SQL*Plus                                                             11.2.0.1.0
SQL*Plus Files for Instant Client                                    11.2.0.1.0

更新:经过几天试图找出问题并找不到问题,我决定继续前进,寻找不同的技术。克里斯蒂安的伟大建议没有产生任何结果,并且解决像贾斯汀所暗示的那种无疑的交易,不幸的是,它并没有让我更进一步。我知道有几个Oracle DBA在工作中最初愿意提供帮助,但是一旦我提到.NET,他们很快就会把我赶走。

1 个答案:

答案 0 :(得分:0)

您正在使用的数据库版本中存在可能相关的错误。您可以通过执行以下SYSDBA来检查是否是这种情况: 更改系统设置事件&#39; 10867永远跟踪名称上下文,级别1&#39;;

这将持续到关机。如果问题消失,你就会遇到错误。

你不应该打开它,而应该升级数据库,并修补ODP.NET。