/// <summary> /// Read messages containing the given type T off the defined /// error queue and moves them to the user defined read queue /// </summary> public void ReturnAllErrorMessages() { GuardAgainstInvalidReadQueue(); GuardAgainstInvalidErrorQueue(); try { // configure the pipeline for return all message to their original queue var pipe = new PipeLine <MessageContext>(); pipe.AddAspect(new TransactionAspect <MessageContext>()); pipe.AddAspect(new LoggingAspect <MessageContext>()); pipe.Register(new ReturnToSource()); foreach (Message message in _errorQueue.PeekAllMessages()) { var ctx = new MessageContext { Message = message, Config = _config, ReadQueue = _readQueue, ErrorQueue = _errorQueue, OpType = ReturnOperation, OnStep = LogMessage }; pipe.Invoke(ctx); } } catch (Exception ex) { throw new BusException($"A problem occurred retreiving messages from the error queue: {ex}"); } }
/// <summary> /// Takes a given type T and serializes it to json before /// wrapping it in an MSMQ message and placing it on a queue. /// For the lifetime of the bus, if muliple queues are defined then each time Send is invoked: /// a) if AutoDistributeOnSend is true then a message is placed on the next queue in the list in a round-robin fashion /// b) otherwise a message is placed on all queues /// </summary> public void Send <T>(T dto) { GuardAgainstInvalidWriteQueues(); GuardAgainstInvalidErrorQueue(); // configure the pipeline for sending a message var pipe = new PipeLine <MessageContext>(); pipe.AddAspect(new TransactionAspect <MessageContext>()); pipe.AddAspect(new LoggingAspect <MessageContext>()); pipe.AddAspect(new MoveToErrorQueueAspect <MessageContext>()); pipe.Register(new SendMessage()); if (_config.AutoDistributeOnSend) { var message = CreateMsmqMessageFromDto(dto); var ctx = new MessageContext { Message = message, Config = _config, WriteQueue = _writeQueues.ElementAt(_nextQueue), ErrorQueue = _errorQueue, OpType = SendOperation, OnStep = LogMessage, OnComplete = SetNextQueueIndex }; pipe.Invoke(ctx); } else { foreach (var writeQueue in _writeQueues) { var message = CreateMsmqMessageFromDto(dto); var ctx = new MessageContext { Message = message, Config = _config, WriteQueue = writeQueue, ErrorQueue = _errorQueue, OpType = SendOperation, OnStep = LogMessage }; pipe.Invoke(ctx); } } }
/// <summary> /// Read specific message off the given read queue identified by the messageId parameter and copy it to one or more defined write queues /// </summary> /// <param name="messageId"></param> public void Copy(string messageId) { GuardAgainstInvalidReadQueue(); GuardAgainstInvalidWriteQueues(); try { // configure the pipeline for copying messages var pipe = new PipeLine <MessageContext>(); pipe.AddAspect(new TransactionAspect <MessageContext>()); pipe.AddAspect(new LoggingAspect <MessageContext>()); pipe.Register(new CopyMessage()); var message = _readQueue.PeekMessageBy(messageId); foreach (var queue in _writeQueues) { var ctx = new MessageContext { Message = message, Config = _config, ReadQueue = _readQueue, WriteQueue = queue, OpType = CopyOperation, OnStep = LogMessage }; pipe.Invoke(ctx); } } catch (Exception ex) { throw new BusException($"A problem occurred copying message: {messageId} - error: {ex}"); } }
/// <summary> /// Peek specific message off the given read queue identified by the messageId parameter and return the body as a string /// </summary> public string ViewMessageBody(string messageId) { GuardAgainstInvalidReadQueue(); try { string bodyAsString = string.Empty; var pipe = new PipeLine <MessageContext>(); pipe.AddAspect(new TransactionAspect <MessageContext>()); pipe.AddAspect(new LoggingAspect <MessageContext>()); pipe.Register(new ViewMessage(s => bodyAsString = s)); var message = _readQueue.PeekMessageBy(messageId); var ctx = new MessageContext { Message = message, Config = _config, ReadQueue = _readQueue, OpType = ViewOperation, OnStep = LogMessage }; ctx.Message.Formatter = new BodyAsStringFormatter(); pipe.Invoke(ctx); return(bodyAsString); } catch (Exception ex) { throw new BusException($"A problem occurred viewing message: {messageId} - error: {ex}"); } }
/// <summary> /// Takes a given type T and serializes it to json before wrapping it in an MSMQ message and placing it on a queue. /// For the lifetime of the bus, if muliple queues are defined then each time Send is invoked: /// a) if a destination has been passed in, send to that queue only /// b) or if AutoDistributeOnSend is true then a message is placed on the next queue in the list in a round-robin fashion /// c) otherwise a message is placed on all queues /// </summary> public void Send <T>(T dto, string destination = "") { GuardAgainstInvalidWriteQueues(); GuardAgainstInvalidErrorQueue(); destination = stripRemoteFrom(destination); // configure the pipeline for sending a message var pipe = new PipeLine <MessageContext>(); pipe.AddAspect(new TransactionAspect <MessageContext>()); pipe.AddAspect(new LoggingAspect <MessageContext>()); pipe.AddAspect(new MoveToErrorQueueAspect <MessageContext>()); pipe.Register(new SendMessage()); // if destination passed in override the configured bus, assert that the destination is known and send to it if (!string.IsNullOrEmpty(destination) && !string.IsNullOrWhiteSpace(destination)) { GuardAgainstUnknownQueue(destination); var queue = _writeQueues.First(q => q.FormatName.EndsWith(destination)); var message = CreateMsmqMessageFromDto(dto); var ctx = new MessageContext { Message = message, Config = _config, WriteQueue = queue, ErrorQueue = _errorQueue, OpType = SendOperation, OnStep = LogMessage, OnComplete = SetNextQueueIndex }; pipe.Invoke(ctx); return; } // if applicable, send to the next queue as we carry out load balancing in a round robin fashion if (_config.AutoDistributeOnSend) { var message = CreateMsmqMessageFromDto(dto); var ctx = new MessageContext { Message = message, Config = _config, WriteQueue = _writeQueues.ElementAt(_nextQueue), ErrorQueue = _errorQueue, OpType = SendOperation, OnStep = LogMessage, OnComplete = SetNextQueueIndex }; pipe.Invoke(ctx); return; } // otherwise send the same message to all defined write queues foreach (var writeQueue in _writeQueues) { var message = CreateMsmqMessageFromDto(dto); var ctx = new MessageContext { Message = message, Config = _config, WriteQueue = writeQueue, ErrorQueue = _errorQueue, OpType = SendOperation, OnStep = LogMessage }; pipe.Invoke(ctx); } }
/// <summary> /// Reads messages off a queue, deserializes them into the /// specified type T and invokes registered handlers. Useful when /// you want to control when messages are processed i.e. at a set /// time every day, for example. /// </summary> public void Receive <T>() { GuardAgainstInvalidReadQueue(); GuardAgainstInvalidErrorQueue(); // configure the pipeline for receiving messages var pipe = new PipeLine <MessageContext>(); pipe.AddAspect(new FailFastAspect <MessageContext>()); pipe.AddAspect(new DiscardAspect <MessageContext>()); pipe.AddAspect(new TransactionAspect <MessageContext>()); pipe.AddAspect(new LoggingAspect <MessageContext>()); pipe.AddAspect(new MoveToErrorQueueAspect <MessageContext>()); pipe.AddAspect(new RemoveFromReadQueueAspect <MessageContext>()); pipe.AddAspect(new RetryAspect <MessageContext>()); pipe.Register(new InvokeUserHandlers <T>()); foreach (Message message in _readQueue.PeekAllMessages()) { var ctx = new MessageContext { Message = message, Config = _config, ReadQueue = _readQueue, ErrorQueue = _errorQueue, Handlers = _handlers, OpType = ReceiveOperation, OnStep = LogMessage }; pipe.Invoke(ctx); if (ctx.FailFast) { break; } } }
/// <summary> /// Reads messages off a queue as they arrive, deserializes them into the /// specified type T and invokes registered handlers. This operation is /// asynchnronous meaning registered handlers will be invoked on the /// background thread. /// </summary> /// <typeparam name="T"></typeparam> public void ReceiveAsync <T>() { GuardAgainstInvalidReadQueue(); GuardAgainstInvalidErrorQueue(); // configure the pipeline for receiving messages var pipe = new PipeLine <MessageContext>(); pipe.AddAspect(new FailFastAspect <MessageContext>()); pipe.AddAspect(new DiscardAspect <MessageContext>()); pipe.AddAspect(new TransactionAspect <MessageContext>()); pipe.AddAspect(new LoggingAspect <MessageContext>()); pipe.AddAspect(new MoveToErrorQueueAspect <MessageContext>()); pipe.AddAspect(new RemoveFromReadQueueAspect <MessageContext>()); pipe.AddAspect(new RetryAspect <MessageContext>()); pipe.Register(new InvokeUserHandlers <T>()); _readQueue.ReceiveAsync(message => { var ctx = new MessageContext { Message = message, Config = _config, ReadQueue = _readQueue, ErrorQueue = _errorQueue, Handlers = _handlers, OpType = ReceiveOperation, OnStep = LogMessage }; pipe.Invoke(ctx); if (ctx.FailFast) { LogMessage("Invoking StopReceiveAsync because FailFast equals true"); _readQueue.StopReceiveAsync(); } }); LogMessage("Receiving..."); }
/// <summary> /// Read specific message off the defined error queue and move it to the user defined read queue /// </summary> public void ReturnErrorMessage(string id) { GuardAgainstInvalidReadQueue(); GuardAgainstInvalidErrorQueue(); try { // configure the pipeline for return a message to its original queue var pipe = new PipeLine <MessageContext>(); pipe.AddAspect(new TransactionAspect <MessageContext>()); pipe.AddAspect(new LoggingAspect <MessageContext>()); pipe.Register(new ReturnToSource()); Message message = _errorQueue.PeekMessageBy(id); var ctx = new MessageContext { Message = message, Config = _config, ReadQueue = _readQueue, ErrorQueue = _errorQueue, OpType = ReturnOperation, OnStep = LogMessage }; pipe.Invoke(ctx); } catch (Exception) { throw new BusException($"Message with id {id} was not found on the error queue"); } }