UpdateContextSearchData Add(UpdateContext updateContext, TaskCompletionSource <Update> taskCompletionSource, UpdateValidatorDelegate updateValidator)
        {
            var chatId   = updateContext.ChatId.Identifier;
            var botId    = updateContext.Bot.BotId;
            var prevData = _searchBag.TryRemove(chatId, botId);

            if (prevData != null)
            {
                _logger.LogInformation(
                    "UpdateContext '{0}' cancelled while adding new context with same chatId and botId.",
                    prevData.Value.CurrentUpdateContext
                    );
                SetCancelled(prevData.Value);
            }
            var sd = new UpdateContextSearchData
            {
                CurrentUpdateContext = updateContext,
                ChatId = chatId,
                BotId  = botId,
                TaskCompletionSource = taskCompletionSource,
                UpdateValidator      = updateValidator
            };

            _searchBag.Add(sd);
            _logger.LogTrace(
                "UpdateContext '{0}' added to SearchBag of read-callbacks.",
                updateContext
                );
            return(sd);
        }
        void SetException(UpdateContextSearchData searchData, Exception ex)
        {
            var taskCompletionSource = searchData.TaskCompletionSource;
            var origCtx = searchData.CurrentUpdateContext;

            _searchBag.TryRemove(origCtx.ChatId.Identifier, origCtx.Bot.BotId);

            //Task will be continued with current thread context of readed UpdateContext object.
            //So we don`t need to start it with IExecutionManager.
            var t = taskCompletionSource.Task;

            if (t.IsCanceled || t.IsCompleted || t.IsFaulted)
            {
                return;
            }
            taskCompletionSource.SetException(ex);
        }