/// <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> /// 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> /// 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> /// 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> /// 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> /// 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}"); } }
public void InvokeTest() { var source = new[] { 1, 2, 3, 4, 5 }; using (var p = new PipeLine(CancellationToken.None)) { var first = p.Add(1, 10, source, i => i * 10); var second = p.AddSelectMany(1, 100, first.Results, i => Enumerable.Range(0, i)); var list = new List <int>(); p.Invoke(() => { list.AddRange(second.Results.GetConsumingEnumerable()); }); foreach (var x in list.OrderBy(x => x).Zip(source.Select(x => x * 10).SelectMany(x => Enumerable.Range(0, x)).OrderBy(x => x), (s, t) => new { s, t })) { Assert.Equal(x.s, x.t); } } }
/// <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"); } }