代码之家  ›  专栏  ›  技术社区  ›  Artur S.

数据库远程事件和异步编程

  •  1
  • Artur S.  · 技术社区  · 6 年前

    我有一个同步方法,它调用一个方法,该方法整理自定义对象上的一堆数据,并将其存储在服务器上Firebird数据库的表条目中。

    在服务器上,监控进程使用数据库事件(表触发器引发由监控器捕获的事件)持续监视第一个表中的新条目。当引发此事件时,该数据将发送到第三方黑盒服务,以使用专有库进行处理,这需要大约零到1分钟的时间来回复。

    第三方服务使用一些数据进行响应,这些数据输入到数据库的第二个表中。第二张桌子有 另一个 触发客户端程序监视的。客户机的程序必须等到第三方回复某些数据,或者超时(相同的1分钟)。

    我目前正在深入研究数据库事件的世界,我已经陷入了僵局:

    目前,我有一个运行同步方法的按键,它根据应用程序设置运行另一个运行完美的同步方法,或者另一个在Firebird数据库中插入条目的方法。此数据库由另一个进程监视,该进程读取该条目,执行一些操作,并在另一个表中插入新数据。

    回到主程序,我现在拥有的是这个方法有一个事件处理程序,它在插入新数据时被触发。但是,由于它是一个事件,方法的其余部分将运行其过程,在事件处理程序有机会读取新数据之前提前结束。

    在伪代码中:

    MainWindow_KeyDown(object sender, EventArgs e)
    {
        if (e.Key == X)
        {
            MakeADecision()
        }
    }
    
    MakeADecision()
    {
        if (Properties.Settings.Default.MySetting) Console.Write(DoLocalStuff());
        else Console.Write(DoRemoteStuff());
    }
    
    string DoRemoteStuff()
    {
        using (OldDataTableAdapter)
        using (NewDataTableAdapter)
        {
            OldDataTableAdapter.Insert(OldData);
            var revent = new FBRemoteEvent(MyConnectionString);
            revent.RemoteEventCounts += (sender, e) =>
            {
                NewDataTableAdapter.Fill(NewDataDataTable);
                NewData = NewDataDataTable[0].MYCOLUMN;
            };
            revent.QueueEvents("MY_FB_EVENT");
        }
        return NewData;
    }
    

    如你所见,这里的问题是 DoRemoteStuff 到达它的归宿 之前 可以触发事件。我试着转身 DoRemoteStuff() 进入异步方法,但我不知道如何将事件与异步方法一起使用。有人能帮我吗?关于如何使用异步方法有什么提示或提示吗?

    1 回复  |  直到 6 年前
        1
  •  0
  •   Mark Rotteveel    6 年前

    可能的解决方案是使用 TaskCompletionSource 因此,您可以将方法转换为异步方法。这是基于 Is it possible to await an event instead of another async method? .

    MakeADecision()
    {
        if (Properties.Settings.Default.MySetting)
        { 
            Console.Write(DoLocalStuff());
        }
        else
        {
            // Consider making MakeADecision async as well
            NewData = DoRemoteStuff().Result;
            Console.Write(NewData);
        }
    }
    
    async Task<string> DoRemoteStuff()
    {
        Task<string> task;
        using (OldDataTableAdapter)
        {
            OldDataTableAdapter.Insert(OldData);
            task = WaitForEvent(MyConnectionString);
        }
        return await task;
    }
    
    private async Task<string> WaitForEvent(string connectionString)
    {
        var taskCompletionSource = new TaskCompletionSource<string>();
        var revent = new FbRemoteEvent(connectionString);
        revent.RemoteEventCounts += (sender, e) =>
        {
            using (NewDataTableAdapter)
            {
                NewDataTableAdapter.Fill(NewDataDataTable);
                string newData = NewDataDataTable[0].MYCOLUMN;
                taskCompletionSource.SetResult(newData);
            }
            sender.Dispose();
        };
        revent.QueueEvents("MY_FB_EVENT");
    
        return await taskCompletionSource.Task;
    }
    

    需要指出的一些事情:

    • 您需要显式地释放事件以避免内存泄漏
    • 这个 using 对于 NewDataTableAdapter 属于事件处理程序
    • 这个 MakeADecision 方法似乎也是异步的候选方法

    一句警告,我的C有点生锈(而且我从来没有做过太多 async ,所以我不确定这是否是惯用的方法。我也没有像上面写的那样测试代码(我编写并测试了一个更简单的版本,但是我可能在将代码转换为类似的解决方案时引入了错误)。

    此解决方案还可能在插入触发事件的新数据和注册事件之间存在竞争条件(除非 Dispose 使用 块是提交数据的内容),请考虑移动 WaitForEvent 插入之前。还应考虑是否有可能从另一个更改完成的更新中接收事件。