Проблемы с использованием async, await

345
08 октября 2017, 21:47

Здравствуйте, форумчане)) У меня такой вопрос: написал бота для telegram, который извлекает определенную информацию из фотографии и заносит ее в базу данных. Далее код, после- пояснение.

 private static async void BotOnPhotoReceived(object sender, MessageEventArgs messageEventArgs)
    {
        var message = messageEventArgs.Message;
        try
        {
            if (message == null || message.Type != MessageType.PhotoMessage)
                return;

            var fileId = message.Photo[message.Photo.Length - 1].FileId;
            var file = await Bot.GetFileAsync(fileId);
            var stream = file.FileStream;
            using (Stream output = new FileStream($"../../Photo/img{message.Chat.Id}{fileId}.jpg", FileMode.Append))
            {
                byte[] buffer = new byte[32 * 1024];
                int read;
                while ((read = stream.Read(buffer, 0, buffer.Length)) > 0)
                {
                    output.Write(buffer, 0, read);
                }
            }
            await Bot.SendChatActionAsync(message.Chat.Id, ChatAction.Typing);
            var FileUrl = @"D:\\128.png";
            using (var streamm = System.IO.File.Open(FileUrl, FileMode.Open))
            {
                FileToSend fts = new FileToSend();
                fts.Content = streamm;
                fts.Filename = FileUrl.Split('\\').Last();
                var test = await Bot.SendStickerAsync(message.Chat.Id, fts);
            }
            string imagePath = $"../../Photo/img{message.Chat.Id}{fileId}.jpg";
            TextDetection newTD = new TextDetection();
            string text = newTD.photo2string(imagePath);
            string result = ParseString(text);
            Console.WriteLine(Convert.ToDouble(result));
            double result1 = Math.Abs(Convert.ToDouble(result));
            DataBaseCon.InsertUser((int)message.Chat.Id, message.Chat.FirstName);
            DataBaseCon.InsertAmount((int)message.Chat.Id, result1);
            var keyboard = new Telegram.Bot.Types.ReplyMarkups.InlineKeyboardMarkup();
            keyboard.InlineKeyboard = new Telegram.Bot.Types.InlineKeyboardButtons.InlineKeyboardButton[][]
            {
                new Telegram.Bot.Types.InlineKeyboardButtons.InlineKeyboardButton[]
                {
                    new KeyboardButton("Продукты питания"),
                    new KeyboardButton("Техника"),
                },
                new Telegram.Bot.Types.InlineKeyboardButtons.InlineKeyboardButton[]
                {
                    new KeyboardButton("Транспорт"),
                    new KeyboardButton("Мобильная связь")
                },
                new Telegram.Bot.Types.InlineKeyboardButtons.InlineKeyboardButton[]
                {
                    new KeyboardButton("Другое")
                },
            };
            await Bot.SendTextMessageAsync(message.Chat.Id, "Выберите категорию товара, который вы приобрели", replyMarkup: keyboard);

            await Task.Delay(1000);
            bool flag = true;
            Bot.OnCallbackQuery += (object sc, CallbackQueryEventArgs ev) =>
            {
                Task.Delay(5000);
                if (ev.CallbackQuery.Data == "Транспорт" && flag)
                {
                    DataBaseCon.InsertCategory((int)message.Chat.Id, "Транспорт");
                    Bot.AnswerCallbackQueryAsync(ev.CallbackQuery.Id, "Расходы на транспорт зафиксированы");
                    Bot.AnswerCallbackQueryAsync(ev.CallbackQuery.Id);
                }
                else
                if (ev.CallbackQuery.Data == "Мобильная связь" && flag)
                {
                    DataBaseCon.InsertCategory((int)message.Chat.Id, "Мобильная связь");
                    Bot.AnswerCallbackQueryAsync(ev.CallbackQuery.Id, "Расходы на мобильную связь зафиксированы");
                    Bot.AnswerCallbackQueryAsync(ev.CallbackQuery.Id);
                }
                else
                if (ev.CallbackQuery.Data == "Продукты питания" && flag)
                {
                    DataBaseCon.InsertCategory((int)message.Chat.Id, "Продукты питания");
                    Bot.AnswerCallbackQueryAsync(ev.CallbackQuery.Id, "Расходы на продукты питания зафиксированы");
                    Bot.AnswerCallbackQueryAsync(ev.CallbackQuery.Id);
                }
                else
                if (ev.CallbackQuery.Data == "Техника" && flag)
                {
                    DataBaseCon.InsertCategory((int)message.Chat.Id, "Техника");
                    Bot.AnswerCallbackQueryAsync(ev.CallbackQuery.Id, "Расходы на технику зафиксированы");
                    Bot.AnswerCallbackQueryAsync(ev.CallbackQuery.Id);
                }
                else
                if (ev.CallbackQuery.Data == "Другое" && flag)
                {
                    DataBaseCon.InsertCategory((int)message.Chat.Id, "Другое");
                    Bot.AnswerCallbackQueryAsync(ev.CallbackQuery.Id, "Расходы на другую категорию зафиксированы");
                    Bot.AnswerCallbackQueryAsync(ev.CallbackQuery.Id);
                }
                flag = !flag;
            };
            if (flag)
            {  
                await Bot.SendTextMessageAsync(message.Chat.Id, WalletKeeper.Constants.IT_IS_DONE);
            }
        }
        catch (Exception e){
            await Bot.SendTextMessageAsync(message.Chat.Id, WalletKeeper.Constants.FAILED);            
        }
    }

Как видите, я тут использую кнопки и с ними проблема(вернее, с их обработкой). Я загружаю фотографию, после чего выбираю, в какую категорию внести данные(в БД) "Транспорт", "Мобильная связь" e.t.c. и у меня возникает следующая проблема: допустим, загружаю фото в первый раз и выбираю категорию- все хорошо, во второй раз- тоже информация вводится корректно, а в третий раз у меня одни данные вносятся в БД дважды, уже всю голову сломал(и дебаггер тоже), но баг так и не нашел. Предположительно, проблемы из-за неправильного применения async & await. На всякий случай выложу еще код

        static void Main(string[] args)
    {
        Bot.OnMessage += BotOnMessageReceived;
        Bot.OnMessage += BotOnPhotoReceived;
        var me = Bot.GetMeAsync().Result;
        Console.Title = me.Username;
        Bot.StartReceiving();
        Console.ReadLine();
        Bot.StopReceiving();
    }

Заранее спасибо))

Answer 1

Ваша ошибка в том, что внутри обработчика BotOnPhotoReceived вы каждый раз заново подписываетесь на OnCallbackQuery. Таким образом, с каждой новой полученной картинкой, у вас на собитие OnCallbackQuery вешается всё больше и больше одинаковых обработчиков. При наступлении события, они все срабатывают поочередно.

Просто вынесите эту часть кода наружу из BotOnPhotoReceived.

Bot.OnCallbackQuery += (object sc, CallbackQueryEventArgs ev) =>
{
    //...
}

Сделайте аналогично тому, как вы подписываетесь на BotOnMessageReceived и BotOnPhotoReceived. То есть весь код из лямбда-выражения поместите в обычный метод, и подпишите его на событие один раз в начале программы. В методе BotOnPhotoReceived вообще не трогайте Bot.OnCallbackQuery (уберите оттуда все упоминания). Вот приблизительный вид того, что должно получиться:

static void Main(string[] args)
{
    // ...
    Bot.OnCallbackQuery += BotOnCallbackQuery;
    // ...
}
private static void BotOnCallbackQuery(object sender, CallbackQueryEventArgs ev)
{
    if (ev.CallbackQuery.Data == "Транспорт")
    {
        DataBaseCon.InsertCategory((int)message.Chat.Id, "Транспорт");
        Bot.AnswerCallbackQueryAsync(ev.CallbackQuery.Id, "Расходы на транспорт зафиксированы");
        Bot.AnswerCallbackQueryAsync(ev.CallbackQuery.Id);
    }
    else if (ev.CallbackQuery.Data == "Мобильная связь")
    {
        DataBaseCon.InsertCategory((int)message.Chat.Id, "Мобильная связь");
        Bot.AnswerCallbackQueryAsync(ev.CallbackQuery.Id, "Расходы на мобильную связь зафиксированы");
        Bot.AnswerCallbackQueryAsync(ev.CallbackQuery.Id);
    }
    else if (ev.CallbackQuery.Data == "Продукты питания")
    {
        DataBaseCon.InsertCategory((int)message.Chat.Id, "Продукты питания");
        Bot.AnswerCallbackQueryAsync(ev.CallbackQuery.Id, "Расходы на продукты питания зафиксированы");
        Bot.AnswerCallbackQueryAsync(ev.CallbackQuery.Id);
    }
    else if (ev.CallbackQuery.Data == "Техника")
    {
        DataBaseCon.InsertCategory((int)message.Chat.Id, "Техника");
        Bot.AnswerCallbackQueryAsync(ev.CallbackQuery.Id, "Расходы на технику зафиксированы");
        Bot.AnswerCallbackQueryAsync(ev.CallbackQuery.Id);
    }
    else if (ev.CallbackQuery.Data == "Другое")
    {
        DataBaseCon.InsertCategory((int)message.Chat.Id, "Другое");
        Bot.AnswerCallbackQueryAsync(ev.CallbackQuery.Id, "Расходы на другую категорию зафиксированы");
        Bot.AnswerCallbackQueryAsync(ev.CallbackQuery.Id);
    }
}

(Это не готовое решение, а просто пример. Как видите, я убрал из кода переменную flag т.к. не понимаю для чего она у вас служит.)

READ ALSO
Имеет ли смысл мой (апплет?) для окон?

Имеет ли смысл мой (апплет?) для окон?

Всем доброго времени суток! Недавно написал небольшой (я правда не знаю, как это классифицировать) на js

239
Как обойти CORS

Как обойти CORS

Пытаюсь с помощью js забрать ленту RSS следующим способом:

447
Использование слайсов в JS

Использование слайсов в JS

Есть ли возможность в JS использовать слайсы в таком кейсе:

238