protected virtual async void OnMessageCallback(IReceiverLink receiver, Message message)
        {
            var messageType = message.ApplicationProperties[MESSAGE_TYPE_KEY] as string;

            using (Logger.BeginScope(new Dictionary <string, object> {
                ["CorrelationId"] = message.Properties.CorrelationId,
                ["MessageId"] = message.Properties.MessageId,
                ["MessageType"] = messageType
            })) {
                Logger.LogInformation($"Received message {message.Properties.MessageId}");
                try {
                    string rawBody = null;
                    // Get the body
                    if (message.Body is string)
                    {
                        rawBody = message.Body as string;
                    }
                    else if (message.Body is byte[])
                    {
                        using (var reader = XmlDictionaryReader.CreateBinaryReader(
                                   new MemoryStream(message.Body as byte[]),
                                   null,
                                   XmlDictionaryReaderQuotas.Max)) {
                            var doc = new XmlDocument();
                            doc.Load(reader);
                            rawBody = doc.InnerText;
                        }
                    }
                    else
                    {
                        throw new ArgumentException($"Message {message.Properties.MessageId} has body with an invalid type {message.Body.GetType()}");
                    }

                    Logger.LogTrace($"Received message {message.Properties.MessageId} with body: {rawBody}");
                    Logger.LogDebug($"Event type key: {messageType}");

                    if (!EventTypeLookup.ContainsKey(messageType))
                    {
                        Logger.LogError($"Message {message.Properties.MessageId} rejected because message type was not registered for type {messageType}");
                        receiver.Reject(message);
                    }


                    var dataType = EventTypeLookup[messageType];
                    Logger.LogDebug($"Event type: {dataType}");
                    var handlerType = typeof(IDomainEventHandler <>).MakeGenericType(dataType);
                    Logger.LogDebug($"Event type handler interface: {handlerType}");
                    var handler = Provider.GetService(handlerType);

                    if (handler != null)
                    {
                        Logger.LogDebug($"Event type handler: {handler.GetType()}");

                        List <string> errors = new List <string>();
                        var           data   = JsonConvert.DeserializeObject(rawBody, dataType,
                                                                             new JsonSerializerSettings {
                            Error = (object sender, Newtonsoft.Json.Serialization.ErrorEventArgs args) => {
                                errors.Add(args.ErrorContext.Error.Message);
                                args.ErrorContext.Handled = true;
                            }
                        });
                        if (errors.Any())
                        {
                            Logger.LogError($"Message {message.Properties.MessageId} rejected because of errors deserializing messsage body: {string.Join(", ", errors)}");
                            receiver.Reject(message);
                            return;
                        }

                        Logger.LogDebug($"Successfully deserialized body to {dataType}");

                        var eventType = typeof(DomainEventMessage <>).MakeGenericType(dataType);
                        var method    = handlerType.GetTypeInfo().GetMethod("Handle", new Type[] { eventType });

                        if (method != null)
                        {
                            dynamic domainEvent = Activator.CreateInstance(eventType);
                            domainEvent.MessageId     = message.Properties.MessageId;
                            domainEvent.CorrelationId = message.Properties.CorrelationId;
                            domainEvent.Data          = (dynamic)data;
                            await(Task) method.Invoke(handler, new object[] { domainEvent });
                        }

                        Logger.LogInformation($"Message {message.Properties.MessageId} accepted");
                        receiver.Accept(message);
                    }
                    else
                    {
                        Logger.LogError($"Message {message.Properties.MessageId} rejected because handler was not found for type {messageType}");
                        receiver.Reject(message);
                    }
                } catch (Exception ex) {
                    Logger.LogError(ex, $"Message {message.Properties.MessageId} rejected because of unhandled exception {ex.Message}");
                    receiver.Reject(message);
                }
            }
        }
Example #2
0
        protected async Task OnMessageCallbackAsync(IReceiverLink receiver, Message message)
        {
            var messageTypeName = message.ApplicationProperties[Constants.MESSAGE_TYPE_KEY] as string;
            var properties      = new Dictionary <string, object> {
                ["CorrelationId"] = message.Properties.CorrelationId,
                ["MessageId"]     = message.Properties.MessageId,
                ["MessageType"]   = messageTypeName
            };

            // if message has correlationId, set it so that handling can be found by initial correlation
            if (!string.IsNullOrWhiteSpace(message.Properties.CorrelationId))
            {
                CorrelationContext.SetCorrelationId(message.Properties.CorrelationId);
            }

            var timer = new Stopwatch();

            timer.Start();

            using (Logger.BeginScope(properties)) {
                Logger.LogInformation($"Received message {message.Properties.MessageId}");

                try {
                    string body = DomainEventMessage.GetBody(message);
                    Logger.LogTrace("Received message {MessageId} with body: {MessageBody}", message.Properties.MessageId, body);

                    Logger.LogDebug($"Event type key: {messageTypeName}");
                    if (!EventTypeLookup.ContainsKey(messageTypeName))
                    {
                        Logger.LogError($"Message {message.Properties.MessageId} rejected because message type was not registered for type {messageTypeName}");
                        receiver.Reject(message);
                        return;
                    }

                    var dataType = EventTypeLookup[messageTypeName];
                    Logger.LogDebug($"Event type: {dataType}");
                    var handlerType = typeof(IDomainEventHandler <>).MakeGenericType(dataType);
                    Logger.LogDebug($"Event type handler interface: {handlerType}");
                    var handler = Provider.GetService(handlerType);
                    if (handler == null)
                    {
                        Logger.LogError($"Message {message.Properties.MessageId} rejected because handler was not found for type {messageTypeName}");
                        receiver.Reject(message);
                        return;
                    }
                    Logger.LogDebug($"Event type handler: {handler.GetType()}");

                    dynamic domainEvent;
                    try {
                        domainEvent = DomainEventMessage.CreateGenericInstance(dataType, message);
                        Logger.LogDebug($"Successfully deserialized body to {dataType}");
                    } catch (Exception ex) {
                        Logger.LogError(ex, ex.Message);
                        receiver.Reject(message);
                        return;
                    }

                    HandlerResult result;
                    dynamic       dhandler = handler;
                    try {
                        result = await dhandler.HandleAsync(domainEvent).ConfigureAwait(false);
                    } catch (Exception ex) {
                        Logger.LogError(ex, $"Message {message.Properties.MessageId} caught unhandled exception {ex.Message}");
                        result = HandlerResult.Failed;
                    }

                    timer.Stop();
                    var duration = timer.ElapsedMilliseconds;

                    var dp = new Dictionary <string, object> {
                        ["duration"] = duration
                    };

                    using (Logger.BeginScope(dp)) {
                        Logger.LogInformation($"Handler executed for message {message.Properties.MessageId} and returned result of {result}");

                        switch (result)
                        {
                        case HandlerResult.Success:
                            receiver.Accept(message);
                            Logger.LogInformation($"Message {message.Properties.MessageId} accepted");
                            break;

                        case HandlerResult.Retry:
                            var deliveryCount = message.Header.DeliveryCount;
                            var delay         = 10 * deliveryCount;
                            var scheduleTime  = DateTime.UtcNow.AddSeconds(delay);

                            using (var ts = new TransactionScope()) {
                                var sender = new SenderLink(Link.Session, Settings.AppName + "-retry", Settings.Queue);
                                // create a new message to be queued with scheduled delivery time
                                var retry = new Message(body)
                                {
                                    Header                = message.Header,
                                    Footer                = message.Footer,
                                    Properties            = message.Properties,
                                    ApplicationProperties = message.ApplicationProperties
                                };
                                retry.ApplicationProperties[Constants.SCHEDULED_ENQUEUE_TIME_UTC] = scheduleTime;
                                sender.Send(retry);
                                receiver.Accept(message);
                            }
                            Logger.LogInformation($"Message {message.Properties.MessageId} requeued with delay of {delay} seconds for {scheduleTime}");
                            break;

                        case HandlerResult.Failed:
                            receiver.Reject(message);
                            break;

                        case HandlerResult.Release:
                            receiver.Release(message);
                            break;

                        default:
                            throw new NotImplementedException($"Unknown HandlerResult value of {result}");
                        }
                    }
                } catch (Exception ex) {
                    timer.Stop();
                    var duration = timer.ElapsedMilliseconds;

                    var dp = new Dictionary <string, object> {
                        ["duration"] = duration
                    };

                    using (Logger.BeginScope(dp)) {
                        Logger.LogError(ex, $"Message {message.Properties.MessageId} rejected because of unhandled exception {ex.Message}");
                        receiver.Reject(message);
                    }
                }
            }
        }