Oracle更改通知包含无效操作。

3

安装及环境配置

我创建了一个控制台应用程序,目标框架为.NET 4.7.2,并安装了NuGet包Oracle.ManagedDataAccess 版本12.2.1100(最新/当前版本),该包设置了两个查询的依赖项,以监视对 MYTABLE 的更改,这两个查询具有不同的WHERE子句。

我连接的Oracle数据库服务器运行在本地主机上,版本为12.2.0.1.0。

除了监听通知的控制台应用程序外,我还使用Oracle SQL Developer(v18.1.0.095)实际插入或更新“MYTABLE”中的记录,以强制发出通知。

代码

using (var connection = new OracleConnection("Data Source=//localhost:1521/ORCL;Persist Security Info=True;User ID=SYSTEM;Password=password"))
{
    OracleDependency.Port = 3005;

    var dependency = new OracleDependency();
    dependency.OnChange += (sender, eventArgs) =>
    {
        Console.WriteLine($"Change count: {eventArgs.Details.Rows.Count}");

        // Columns in row: string ResourceName, int Info, string Rowid, long QueryId
        foreach (DataRow row in eventArgs.Details.Rows)
        {
            var resourceName = (string)row["ResourceName"];
            var info = (OracleNotificationInfo)row["Info"];
            var rowId = (string)row["rowid"];
            var queryId = (long)row["QueryId"];

            Console.WriteLine($"{queryId} {info} {rowId} {resourceName}");
        }
    };

    connection.Open();

    var command1 = new OracleCommand("SELECT * FROM MYTABLE WHERE NAME = 'N1'", connection);
    dependency.AddCommandDependency(command1);
    command1.Notification.IsNotifiedOnce = false;
    command1.AddRowid = true;
    command1.ExecuteNonQuery();

    var command2 = new OracleCommand("SELECT * FROM MYTABLE WHERE NAME = 'N2'", connection);
    dependency.AddCommandDependency(command2);
    command2.Notification.IsNotifiedOnce = false;
    command2.AddRowid = true;
    command2.ExecuteNonQuery();

    Console.ReadKey();
}

行为

一旦我在一个事务中触发多个命令依赖项,我会在通知中获得两倍于预期的事件行,并且无法区分触发它的查询ID。

在一个事务中运行此查询:

INSERT INTO MYTABLE (NAME) VALUES ('N1');

输出:

Change count: 1
63 Insert AAAR6CAABAAALohABJ SYSTEM.MYTABLE

在一个事务中运行此查询:
INSERT INTO MYTABLE (NAME) VALUES ('N2');

输出:

Change count: 1
64 Insert AAAR6CAABAAALohABK SYSTEM.MYTABLE

但是,当在一个事务中运行此查询时:

INSERT INTO MYTABLE (NAME) VALUES ('N1');
INSERT INTO MYTABLE (NAME) VALUES ('N2');

我得到了这个输出:
Change count: 4
63 Insert AAAR6CAABAAALohABH SYSTEM.MYTABLE
63 Insert AAAR6CAABAAALohABI SYSTEM.MYTABLE
64 Insert AAAR6CAABAAALohABH SYSTEM.MYTABLE
64 Insert AAAR6CAABAAALohABI SYSTEM.MYTABLE

我期望:

Change count: 2
63 Insert AAAR6CAABAAALohABH SYSTEM.MYTABLE
64 Insert AAAR6CAABAAALohABI SYSTEM.MYTABLE

此外,当我执行一个INSERT触发第一个命令和一个UPDATE触发第二个命令时,对于第一个QueryId(63),我得到了4行INSERT+UPDATE,对于第二个QueryId(64),我也得到了4行INSERT+UPDATE,所以现在我无法区分它们。
将命令依赖关系分离到多个OracleDependency类或甚至多个连接中会导致相同的结果。
我想知道是否有人知道这里发生了什么?

作为一种合理性检查,可以使用sqlplus进行相同的插入操作,以确保SQL Developer没有出现任何问题。此外,请查看是否可以找出任何触发器正在执行这些插入操作,因为它们可能会导致副作用。 - Christian Shay
不一定有帮助,但我现在想知道:如果您在同一事务中运行3个和4个INSERTs,您会分别获得6和8个输出行,还是9和16个? - jsanalytics
不行,因为这正是问题所在。如果我执行一个触发依赖1的UPDATE操作,以及一个触发依赖2的INSERT操作,你将无法区分是哪个依赖触发了INSERT或UPDATE操作。 - huysentruitw
更改通知不能保证只通知相关内容。请查看此问题的答案:https://stackoverflow.com/questions/25199325。 - Hilarion
文档说明在最佳尝试模式下可能会出现误报,而当查询复杂时则选择该模式。虽然没有“复杂”的定义,但我无法想象这个简单的where子句被认为是复杂的,难道是吗? - huysentruitw
显示剩余3条评论
2个回答

0
也许使用同一个OracleDependency实例与两个OracleCommand导致了这种行为。
在AddCommandDependency的Oracle docs中的"remarks"部分说明了这一点,但似乎不是默认配置。
我建议使用两个单独的OracleDependency实例。我们不知道内部如何聚合这两个查询,但我猜想,可能ODP.NET以某种不被“保证”模式支持的语法将这两个查询聚合成一个。
这可能会导致通知以“尽力而为”的模式运行,文档中指出这可能会产生错误的结果。

使用ODP.NET时似乎没有办法知道使用了哪种模式,但我猜测模式是从查询中自动检测出来的,并且每种模式的查询约束都在这里有文档记录。


同一连接上的两个独立的Oracle依赖项给出相同的结果。即使使用两个独立的Oracle连接,我得到的结果与我的问题中的结果相同,所以我认为我们可以排除ODP.NET。 - huysentruitw
你有检查查询的限制条件吗?我能想到的一个问题是,你正在使用"SELECT *",而链接文档指出,“保证”模式需要“它引用的每一列都是数字数据类型或VARCHAR2数据类型。”你真的需要监视每一列的变化吗?并且所有的列都是这两种类型吗?也许为了测试目的,只选择一列,看看情况如何。 - GotGot
是的,这是一个只有两列的测试表,一列是数字类型(ID),另一列是VARCHAR2类型(NAME)。 - huysentruitw

0

尝试检查OracleDependency的QueryBasedNotification。

默认情况下,此属性为true。从Oracle Data Provider for .NET 11.1和Oracle Database 11g release 1(11.1)开始,ODP.NET开发人员可以在行级别上注册查询,而不仅仅是对象级别。只有在所选行或行更改时,应用程序才会收到通知。基于查询的通知为开发人员提供了更多使用客户端缓存数据的细粒度控制,因为他们可以更具体地确定应用程序需要被通知的更改内容。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接