База данных удаленных событий и асинхронное программирование - PullRequest
0 голосов
/ 30 ноября 2018

У меня есть синхронный метод, который вызывает метод, который собирает кучу данных в пользовательском объекте и сохраняет их в записи таблицы в базе данных 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 Ответ

0 голосов
/ 01 декабря 2018

Возможным решением будет использование TaskCompletionSource, чтобы вы могли преобразовать свой метод в асинхронный метод.Это основано на Возможно ли ожидать событие вместо другого асинхронного метода? .

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 в конце блока using не является тем, что фиксирует данные), необходимо учитывать состояние гонки, перед перемещением добавьте WaitForEvent.Также рассмотрите возможность получения события из обновления, выполненного для / другим изменением.

...