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

时间:2018-11-30 12:50:53

标签: c# asynchronous events firebird

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

在服务器上,监视进程使用数据库事件(在表触发器中引发由监视器捕获的事件),一直在第一张表中监视新条目。引发此事件时,该数据将被发送到第三方黑盒服务,以使用专有库进行处理,这需要花费近乎零分钟的时间才能回复。

第三方服务回复一些在数据库的第二个表中输入的数据。第二个表具有另一个触发器,该触发器由客户端程序监视。客户端程序必须等待第三方回复某些数据,否则超时(相同的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 个答案:

答案 0 :(得分:0)

可能的解决方案是使用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的{​​{1}}属于事件处理程序之内
  • NewDataTableAdapter方法似乎也可能成为异步方法

一个警告,我的C#有点生锈(而且我从未对MakeADecision做过很多事),所以我不确定这是否是惯用的方式。我也没有测试上面编写的代码(我编写并测试了一个简单的版本,但是在将您的代码转换为类似的解决方案时,我可能引入了错误)。

此解决方案还可能在插入触发事件的新数据与注册事件之间存在争用条件(除非async块末尾的Dispose是提交数据),请考虑在插入之前移动using。此外,请考虑是否有可能从其他更改完成的更新中接收事件。