/// <summary> /// Publishes the provided <paramref name="event"/> on the event bus. /// </summary> public virtual void Publish <TEvent>(TEvent @event) where TEvent : IEvent <TAuthenticationToken> { DateTimeOffset startedAt = DateTimeOffset.UtcNow; Stopwatch mainStopWatch = Stopwatch.StartNew(); string responseCode = null; bool mainWasSuccessfull = false; bool telemeterOverall = false; IDictionary <string, string> telemetryProperties = new Dictionary <string, string> { { "Type", "Azure/Servicebus" } }; string telemetryName = string.Format("{0}/{1}", @event.GetType().FullName, @event.Id); var telemeteredEvent = @event as ITelemeteredMessage; if (telemeteredEvent != null) { telemetryName = telemeteredEvent.TelemetryName; } telemetryName = string.Format("Event/{0}", telemetryName); try { if (!AzureBusHelper.PrepareAndValidateEvent(@event, "Azure-ServiceBus")) { return; } var privateEventAttribute = Attribute.GetCustomAttribute(typeof(TEvent), typeof(PrivateEventAttribute)) as PrivateEventAttribute; var publicEventAttribute = Attribute.GetCustomAttribute(typeof(TEvent), typeof(PrivateEventAttribute)) as PublicEventAttribute; // We only add telemetry for overall operations if two occured telemeterOverall = publicEventAttribute != null && privateEventAttribute != null; // Backwards compatibility and simplicity bool wasSuccessfull; Stopwatch stopWatch = Stopwatch.StartNew(); if (publicEventAttribute == null && privateEventAttribute == null) { stopWatch.Restart(); responseCode = "200"; wasSuccessfull = false; try { var brokeredMessage = new BrokeredMessage(MessageSerialiser.SerialiseEvent(@event)) { CorrelationId = CorrelationIdHelper.GetCorrelationId().ToString("N") }; brokeredMessage.Properties.Add("Type", @event.GetType().FullName); PublicServiceBusPublisher.Send(brokeredMessage); wasSuccessfull = true; } catch (QuotaExceededException exception) { responseCode = "429"; Logger.LogError("The size of the event being sent was too large or the topic has reached it's limit.", exception: exception, metaData: new Dictionary <string, object> { { "Event", @event } }); throw; } catch (Exception exception) { responseCode = "500"; Logger.LogError("An issue occurred while trying to publish an event.", exception: exception, metaData: new Dictionary <string, object> { { "Event", @event } }); throw; } finally { TelemetryHelper.TrackDependency("Azure/Servicebus/EventBus", "Event", telemetryName, "Default Bus", startedAt, stopWatch.Elapsed, responseCode, wasSuccessfull, telemetryProperties); } Logger.LogDebug(string.Format("An event was published on the public bus with the id '{0}' was of type {1}.", @event.Id, @event.GetType().FullName)); } if (publicEventAttribute != null) { stopWatch.Restart(); responseCode = "200"; wasSuccessfull = false; try { var brokeredMessage = new BrokeredMessage(MessageSerialiser.SerialiseEvent(@event)) { CorrelationId = CorrelationIdHelper.GetCorrelationId().ToString("N") }; brokeredMessage.Properties.Add("Type", @event.GetType().FullName); PublicServiceBusPublisher.Send(brokeredMessage); wasSuccessfull = true; } catch (QuotaExceededException exception) { responseCode = "429"; Logger.LogError("The size of the event being sent was too large.", exception: exception, metaData: new Dictionary <string, object> { { "Event", @event } }); throw; } catch (Exception exception) { responseCode = "500"; Logger.LogError("An issue occurred while trying to publish an event.", exception: exception, metaData: new Dictionary <string, object> { { "Event", @event } }); throw; } finally { TelemetryHelper.TrackDependency("Azure/Servicebus/EventBus", "Event", telemetryName, "Public Bus", startedAt, stopWatch.Elapsed, responseCode, wasSuccessfull, telemetryProperties); } Logger.LogDebug(string.Format("An event was published on the public bus with the id '{0}' was of type {1}.", @event.Id, @event.GetType().FullName)); } if (privateEventAttribute != null) { stopWatch.Restart(); responseCode = "200"; wasSuccessfull = false; try { var brokeredMessage = new BrokeredMessage(MessageSerialiser.SerialiseEvent(@event)) { CorrelationId = CorrelationIdHelper.GetCorrelationId().ToString("N") }; brokeredMessage.Properties.Add("Type", @event.GetType().FullName); PrivateServiceBusPublisher.Send(brokeredMessage); wasSuccessfull = true; } catch (QuotaExceededException exception) { responseCode = "429"; Logger.LogError("The size of the event being sent was too large.", exception: exception, metaData: new Dictionary <string, object> { { "Event", @event } }); throw; } catch (Exception exception) { responseCode = "500"; Logger.LogError("An issue occurred while trying to publish an event.", exception: exception, metaData: new Dictionary <string, object> { { "Event", @event } }); throw; } finally { TelemetryHelper.TrackDependency("Azure/Servicebus/EventBus", "Event", telemetryName, "Private Bus", startedAt, stopWatch.Elapsed, responseCode, wasSuccessfull, telemetryProperties); } Logger.LogDebug(string.Format("An event was published on the private bus with the id '{0}' was of type {1}.", @event.Id, @event.GetType().FullName)); } mainWasSuccessfull = true; } finally { mainStopWatch.Stop(); if (telemeterOverall) { TelemetryHelper.TrackDependency("Azure/Servicebus/EventBus", "Event", telemetryName, null, startedAt, mainStopWatch.Elapsed, responseCode, mainWasSuccessfull, telemetryProperties); } } }
public void Save_ValidEvent_EventCanBeRetreived() { // Arrange var correlationIdHelper = new CorrelationIdHelper(new ThreadedContextItemCollectionFactory()); correlationIdHelper.SetCorrelationId(Guid.NewGuid()); var logger = new ConsoleLogger(new LoggerSettings(), correlationIdHelper); try { // Arrange var connectionStringFactory = new TestMongoEventStoreConnectionStringFactory(); TestMongoEventStoreConnectionStringFactory.DatabaseName = string.Format("Test-{0}", new Random().Next(0, 9999)); var eventStore = new MongoDbEventStore <Guid>(new MongoDbEventBuilder <Guid>(), new MongoDbEventDeserialiser <Guid>(), logger, connectionStringFactory); var event1 = new TestEvent { Rsn = Guid.NewGuid(), Id = Guid.NewGuid(), CorrelationId = correlationIdHelper.GetCorrelationId(), Frameworks = new List <string> { "Test 1" }, TimeStamp = DateTimeOffset.UtcNow }; var event2 = new TestEvent { Rsn = Guid.NewGuid(), Id = Guid.NewGuid(), CorrelationId = correlationIdHelper.GetCorrelationId(), Frameworks = new List <string> { "Test 2" }, TimeStamp = DateTimeOffset.UtcNow }; // Act eventStore.Save <TestEvent>(event1); eventStore.Save <TestEvent>(event2); // Assert var timer = new Stopwatch(); IList <IEvent <Guid> > events = eventStore.Get <TestEvent>(event1.Id).ToList(); timer.Stop(); Console.WriteLine("Load one operation took {0}", timer.Elapsed); Assert.AreEqual(1, events.Count); Assert.AreEqual(event1.Id, events.Single().Id); Assert.AreEqual(event1.Frameworks.Single(), events.Single().Frameworks.Single()); timer.Restart(); events = eventStore.Get <TestEvent>(event2.Id).ToList(); timer.Stop(); Console.WriteLine("Load one operation took {0}", timer.Elapsed); Assert.AreEqual(1, events.Count); Assert.AreEqual(event2.Id, events.Single().Id); Assert.AreEqual(event2.Frameworks.Single(), events.Single().Frameworks.Single()); timer.Restart(); IList <EventData> correlatedEvents = eventStore.Get(event1.CorrelationId).ToList(); timer.Stop(); Console.WriteLine("Load several correlated operation took {0}", timer.Elapsed); Assert.AreEqual(2, correlatedEvents.Count); } finally { // Clean-up TestMongoDataStoreConnectionStringFactory.DatabaseName = TestMongoEventStoreConnectionStringFactory.DatabaseName; var factory = new TestMongoDbDataStoreFactory(logger, new TestMongoDataStoreConnectionStringFactory()); IMongoCollection <TestEvent> collection = factory.GetTestEventCollection(); collection.Database.Client.DropDatabase(TestMongoDataStoreConnectionStringFactory.DatabaseName); } }
public virtual void Publish <TCommand>(TCommand command) where TCommand : ICommand <TAuthenticationToken> { DateTimeOffset startedAt = DateTimeOffset.UtcNow; Stopwatch mainStopWatch = Stopwatch.StartNew(); string responseCode = "200"; bool wasSuccessfull = false; IDictionary <string, string> telemetryProperties = new Dictionary <string, string> { { "Type", "Azure/Servicebus" } }; string telemetryName = string.Format("{0}/{1}", command.GetType().FullName, command.Id); var telemeteredCommand = command as ITelemeteredMessage; if (telemeteredCommand != null) { telemetryName = telemeteredCommand.TelemetryName; } telemetryName = string.Format("Command/{0}", telemetryName); try { if (!AzureBusHelper.PrepareAndValidateCommand(command, "Azure-ServiceBus")) { return; } try { var brokeredMessage = new BrokeredMessage(MessageSerialiser.SerialiseCommand(command)) { CorrelationId = CorrelationIdHelper.GetCorrelationId().ToString("N") }; brokeredMessage.Properties.Add("Type", command.GetType().FullName); PrivateServiceBusPublisher.Send(brokeredMessage); } catch (QuotaExceededException exception) { responseCode = "429"; Logger.LogError("The size of the command being sent was too large.", exception: exception, metaData: new Dictionary <string, object> { { "Command", command } }); throw; } catch (Exception exception) { responseCode = "500"; Logger.LogError("An issue occurred while trying to publish a command.", exception: exception, metaData: new Dictionary <string, object> { { "Command", command } }); throw; } Logger.LogInfo(string.Format("A command was sent of type {0}.", command.GetType().FullName)); wasSuccessfull = true; } finally { mainStopWatch.Stop(); TelemetryHelper.TrackDependency("Azure/Servicebus/CommandBus", "Command", telemetryName, null, startedAt, mainStopWatch.Elapsed, responseCode, wasSuccessfull, telemetryProperties); } }
/// <summary> /// Publishes the provided <paramref name="events"/> on the event bus. /// </summary> public virtual void Publish <TEvent>(IEnumerable <TEvent> events) where TEvent : IEvent <TAuthenticationToken> { IList <TEvent> sourceEvents = events.ToList(); DateTimeOffset startedAt = DateTimeOffset.UtcNow; Stopwatch mainStopWatch = Stopwatch.StartNew(); string responseCode = null; bool mainWasSuccessfull = false; IDictionary <string, string> telemetryProperties = new Dictionary <string, string> { { "Type", "Azure/Servicebus" } }; string telemetryName = "Events"; string telemetryNames = string.Empty; foreach (TEvent @event in sourceEvents) { string subTelemetryName = string.Format("{0}/{1}", @event.GetType().FullName, @event.Id); var telemeteredEvent = @event as ITelemeteredMessage; if (telemeteredEvent != null) { subTelemetryName = telemeteredEvent.TelemetryName; } telemetryNames = string.Format("{0}{1},", telemetryNames, subTelemetryName); } if (telemetryNames.Length > 0) { telemetryNames = telemetryNames.Substring(0, telemetryNames.Length - 1); } telemetryProperties.Add("Events", telemetryNames); try { IList <string> sourceEventMessages = new List <string>(); IList <BrokeredMessage> privateBrokeredMessages = new List <BrokeredMessage>(sourceEvents.Count); IList <BrokeredMessage> publicBrokeredMessages = new List <BrokeredMessage>(sourceEvents.Count); foreach (TEvent @event in sourceEvents) { if (!AzureBusHelper.PrepareAndValidateEvent(@event, "Azure-ServiceBus")) { continue; } var brokeredMessage = new BrokeredMessage(MessageSerialiser.SerialiseEvent(@event)) { CorrelationId = CorrelationIdHelper.GetCorrelationId().ToString("N") }; brokeredMessage.Properties.Add("Type", @event.GetType().FullName); var privateEventAttribute = Attribute.GetCustomAttribute(typeof(TEvent), typeof(PrivateEventAttribute)) as PrivateEventAttribute; var publicEventAttribute = Attribute.GetCustomAttribute(typeof(TEvent), typeof(PrivateEventAttribute)) as PublicEventAttribute; if ( // Backwards compatibility and simplicity (publicEventAttribute == null && privateEventAttribute == null) || publicEventAttribute != null ) { publicBrokeredMessages.Add(brokeredMessage); sourceEventMessages.Add(string.Format("An event was published on the public bus with the id '{0}' was of type {1}.", @event.Id, @event.GetType().FullName)); } if (privateEventAttribute != null) { privateBrokeredMessages.Add(brokeredMessage); sourceEventMessages.Add(string.Format("An event was published on the private bus with the id '{0}' was of type {1}.", @event.Id, @event.GetType().FullName)); } } bool wasSuccessfull; Stopwatch stopWatch = Stopwatch.StartNew(); // Backwards compatibility and simplicity stopWatch.Restart(); responseCode = "200"; wasSuccessfull = false; try { PublicServiceBusPublisher.SendBatch(publicBrokeredMessages); wasSuccessfull = true; } catch (QuotaExceededException exception) { responseCode = "429"; Logger.LogError("The size of the event being sent was too large.", exception: exception, metaData: new Dictionary <string, object> { { "Events", publicBrokeredMessages } }); throw; } catch (Exception exception) { responseCode = "500"; Logger.LogError("An issue occurred while trying to publish an event.", exception: exception, metaData: new Dictionary <string, object> { { "Events", publicBrokeredMessages } }); throw; } finally { TelemetryHelper.TrackDependency("Azure/Servicebus/EventBus", "Event", telemetryName, "Public Bus", startedAt, stopWatch.Elapsed, responseCode, wasSuccessfull, telemetryProperties); } stopWatch.Restart(); responseCode = "200"; wasSuccessfull = false; try { PrivateServiceBusPublisher.SendBatch(privateBrokeredMessages); wasSuccessfull = true; } catch (QuotaExceededException exception) { responseCode = "429"; Logger.LogError("The size of the event being sent was too large.", exception: exception, metaData: new Dictionary <string, object> { { "Events", privateBrokeredMessages } }); throw; } catch (Exception exception) { responseCode = "500"; Logger.LogError("An issue occurred while trying to publish an event.", exception: exception, metaData: new Dictionary <string, object> { { "Events", privateBrokeredMessages } }); throw; } finally { TelemetryHelper.TrackDependency("Azure/Servicebus/EventBus", "Event", telemetryName, "Private Bus", startedAt, stopWatch.Elapsed, responseCode, wasSuccessfull, telemetryProperties); } foreach (string message in sourceEventMessages) { Logger.LogInfo(message); } mainWasSuccessfull = true; } finally { mainStopWatch.Stop(); TelemetryHelper.TrackDependency("Azure/Servicebus/EventBus", "Event", telemetryName, null, startedAt, mainStopWatch.Elapsed, responseCode, mainWasSuccessfull, telemetryProperties); } }
protected override void ReceiveEvent(PartitionContext context, EventData eventData) { // Do a manual 10 try attempt with back-off for (int i = 0; i < 10; i++) { try { Logger.LogDebug(string.Format("An event message arrived with the partition key '{0}', sequence number '{1}' and offset '{2}'.", eventData.PartitionKey, eventData.SequenceNumber, eventData.Offset)); string messageBody = Encoding.UTF8.GetString(eventData.GetBytes()); IEvent <TAuthenticationToken> @event = MessageSerialiser.DeserialiseEvent(messageBody); CorrelationIdHelper.SetCorrelationId(@event.CorrelationId); Logger.LogInfo(string.Format("An event message arrived with the partition key '{0}', sequence number '{1}' and offset '{2}' was of type {3}.", eventData.PartitionKey, eventData.SequenceNumber, eventData.Offset, @event.GetType().FullName)); Type eventType = @event.GetType(); string targetQueueName = eventType.FullName; try { object rsn = eventType.GetProperty("Rsn").GetValue(@event, null); targetQueueName = string.Format("{0}.{1}", targetQueueName, rsn); } catch { Logger.LogDebug(string.Format("An event message arrived with the partition key '{0}', sequence number '{1}' and offset '{2}' was of type {3} but with no Rsn property.", eventData.PartitionKey, eventData.SequenceNumber, eventData.Offset, eventType)); // Do nothing if there is no rsn. Just use @event type name } CreateQueueAndAttachListenerIfNotExist(targetQueueName); EnqueueEvent(targetQueueName, @event); // remove the original message from the incoming queue context.CheckpointAsync(eventData); Logger.LogDebug(string.Format("An event message arrived and was processed with the partition key '{0}', sequence number '{1}' and offset '{2}'.", eventData.PartitionKey, eventData.SequenceNumber, eventData.Offset)); return; } catch (Exception exception) { // Indicates a problem, unlock message in queue Logger.LogError(string.Format("An event message arrived with the partition key '{0}', sequence number '{1}' and offset '{2}' but failed to be process.", eventData.PartitionKey, eventData.SequenceNumber, eventData.Offset), exception: exception); switch (i) { case 0: case 1: // 10 seconds Thread.Sleep(10 * 1000); break; case 2: case 3: // 30 seconds Thread.Sleep(30 * 1000); break; case 4: case 5: case 6: // 1 minute Thread.Sleep(60 * 1000); break; case 7: case 8: case 9: // 3 minutes Thread.Sleep(3 * 60 * 1000); break; } } } // Eventually just accept it context.CheckpointAsync(eventData); }
/// <summary> /// Publishes the provided <paramref name="command"></paramref> and waits for an event of <typeparamref name="TEvent"/> or exits if the specified timeout is expired. /// </summary> /// <param name="command">The <typeparamref name="TCommand"/> to publish.</param> /// <param name="condition">A delegate to be executed over and over until it returns the <typeparamref name="TEvent"/> that is desired, return null to keep trying.</param> /// <param name="millisecondsTimeout">The number of milliseconds to wait, or <see cref="F:System.Threading.Timeout.Infinite"/> (-1) to wait indefinitely.</param> /// <param name="eventReceiver">If provided, is the <see cref="IEventReceiver{TAuthenticationToken}" /> that the event is expected to be returned on.</param> public virtual TEvent PublishAndWait <TCommand, TEvent>(TCommand command, Func <IEnumerable <IEvent <TAuthenticationToken> >, TEvent> condition, int millisecondsTimeout, IEventReceiver <TAuthenticationToken> eventReceiver = null) where TCommand : ICommand <TAuthenticationToken> { DateTimeOffset startedAt = DateTimeOffset.UtcNow; Stopwatch mainStopWatch = Stopwatch.StartNew(); string responseCode = "200"; bool wasSuccessfull = false; IDictionary <string, string> telemetryProperties = new Dictionary <string, string> { { "Type", "Azure/Servicebus" } }; string telemetryName = string.Format("{0}/{1}", command.GetType().FullName, command.Id); var telemeteredCommand = command as ITelemeteredMessage; if (telemeteredCommand != null) { telemetryName = telemeteredCommand.TelemetryName; } telemetryName = string.Format("Command/{0}", telemetryName); TEvent result; try { if (eventReceiver != null) { throw new NotSupportedException("Specifying a different event receiver is not yet supported."); } if (!AzureBusHelper.PrepareAndValidateCommand(command, "Azure-ServiceBus")) { return((TEvent)(object)null); } result = (TEvent)(object)null; EventWaits.Add(command.CorrelationId, new List <IEvent <TAuthenticationToken> >()); try { var brokeredMessage = new BrokeredMessage(MessageSerialiser.SerialiseCommand(command)) { CorrelationId = CorrelationIdHelper.GetCorrelationId().ToString("N") }; brokeredMessage.Properties.Add("Type", command.GetType().FullName); PrivateServiceBusPublisher.Send(brokeredMessage); } catch (QuotaExceededException exception) { responseCode = "429"; Logger.LogError("The size of the command being sent was too large.", exception: exception, metaData: new Dictionary <string, object> { { "Command", command } }); throw; } catch (Exception exception) { responseCode = "500"; Logger.LogError("An issue occurred while trying to publish a command.", exception: exception, metaData: new Dictionary <string, object> { { "Command", command } }); throw; } Logger.LogInfo(string.Format("A command was sent of type {0}.", command.GetType().FullName)); wasSuccessfull = true; } finally { mainStopWatch.Stop(); TelemetryHelper.TrackDependency("Azure/Servicebus/CommandBus", "Command", telemetryName, null, startedAt, mainStopWatch.Elapsed, responseCode, wasSuccessfull, telemetryProperties); } SpinWait.SpinUntil(() => { IList <IEvent <TAuthenticationToken> > events = EventWaits[command.CorrelationId]; result = condition(events); return(result != null); }, millisecondsTimeout, sleepInMilliseconds: 1000); TelemetryHelper.TrackDependency("Azure/Servicebus/CommandBus", "Command/AndWait", string.Format("Command/AndWait{0}", telemetryName.Substring(7)), null, startedAt, mainStopWatch.Elapsed, responseCode, wasSuccessfull, telemetryProperties); return(result); }
/// <summary> /// Create a <see cref="BrokeredMessage"/> with additional properties to aid routing and tracing /// </summary> protected virtual BrokeredMessage CreateBrokeredMessage <TMessage>(Func <TMessage, string> serialiserFunction, Type messageType, TMessage message) { string messageBody = serialiserFunction(message); #if NET452 var brokeredMessage = new BrokeredMessage(messageBody) #endif #if NETSTANDARD2_0 byte[] messageBodyData; using (var stream = new MemoryStream()) { brokeredMessageSerialiser.WriteObject(stream, messageBody); stream.Flush(); stream.Position = 0; messageBodyData = stream.ToArray(); } var brokeredMessage = new BrokeredMessage(messageBodyData) #endif { CorrelationId = CorrelationIdHelper.GetCorrelationId().ToString("N") }; brokeredMessage.AddUserProperty("CorrelationId", brokeredMessage.CorrelationId); brokeredMessage.AddUserProperty("Type", messageType.FullName); brokeredMessage.AddUserProperty("Source", string.Format("{0}/{1}/{2}/{3}", Logger.LoggerSettings.ModuleName, Logger.LoggerSettings.Instance, Logger.LoggerSettings.Environment, Logger.LoggerSettings.EnvironmentInstance)); #if NET452 brokeredMessage.AddUserProperty("Framework", ".NET Framework"); #else brokeredMessage.AddUserProperty("Framework", ".NET Core"); #endif // see https://github.com/Chinchilla-Software-Com/CQRS/wiki/Inter-process-function-security</remarks> string configurationKey = string.Format("{0}.SigningToken", messageType.FullName); string signingToken; HashAlgorithm signer = Signer.Create(); if (!ConfigurationManager.TryGetSetting(configurationKey, out signingToken) || string.IsNullOrWhiteSpace(signingToken)) { if (!ConfigurationManager.TryGetSetting(SigningTokenConfigurationKey, out signingToken) || string.IsNullOrWhiteSpace(signingToken)) { signingToken = Guid.Empty.ToString("N"); } } if (!string.IsNullOrWhiteSpace(signingToken)) { using (var hashStream = new MemoryStream(Encoding.UTF8.GetBytes($"{signingToken}{messageBody}"))) brokeredMessage.AddUserProperty("Signature", Convert.ToBase64String(signer.ComputeHash(hashStream))); } try { var stackTrace = new StackTrace(); StackFrame[] stackFrames = stackTrace.GetFrames(); if (stackFrames != null) { foreach (StackFrame frame in stackFrames) { MethodBase method = frame.GetMethod(); if (method.ReflectedType == null) { continue; } try { if (ExclusionNamespaces.All(@namespace => !method.ReflectedType.FullName.StartsWith(@namespace))) { brokeredMessage.AddUserProperty("Source-Method", string.Format("{0}.{1}", method.ReflectedType.FullName, method.Name)); break; } } catch { // Just move on } } } } catch { // Just move on } return(brokeredMessage); }
protected virtual TServiceResponse CompleteResponse <TServiceResponse>(TServiceResponse serviceResponse) where TServiceResponse : IServiceResponse { serviceResponse.CorrelationId = CorrelationIdHelper.GetCorrelationId(); return(serviceResponse); }
public virtual ICommand <TAuthenticationToken> ReceiveCommand(string messageBody, Action <ICommand <TAuthenticationToken> > receiveCommandHandler, string messageId, Action skippedAction = null, Action lockRefreshAction = null) { ICommand <TAuthenticationToken> command; try { command = MessageSerialiser.DeserialiseCommand(messageBody); } catch (JsonSerializationException exception) { JsonSerializationException checkException = exception; bool safeToExit = false; do { if (checkException.Message.StartsWith("Could not load assembly")) { safeToExit = true; break; } } while ((checkException = checkException.InnerException as JsonSerializationException) != null); if (safeToExit) { const string pattern = @"(?<=^Error resolving type specified in JSON ').+?(?='\. Path '\$type')"; Match match = new Regex(pattern).Match(exception.Message); if (match.Success) { string[] typeParts = match.Value.Split(','); if (typeParts.Length == 2) { string classType = typeParts[0]; bool isRequired = BusHelper.IsEventRequired(string.Format("{0}.IsRequired", classType)); if (!isRequired) { if (skippedAction != null) { skippedAction(); } return(null); } } } } throw; } string commandTypeName = command.GetType().FullName; CorrelationIdHelper.SetCorrelationId(command.CorrelationId); Logger.LogInfo(string.Format("A command message arrived with the {0} was of type {1}.", messageId, commandTypeName)); bool canRefresh; if (!ConfigurationManager.TryGetSetting(string.Format("{0}.ShouldRefresh", commandTypeName), out canRefresh)) { canRefresh = false; } if (canRefresh) { if (lockRefreshAction == null) { Logger.LogWarning(string.Format("A command message arrived with the {0} was of type {1} and was destined to support lock renewal, but no action was provided.", messageId, commandTypeName)); } else { lockRefreshAction(); } } receiveCommandHandler(command); return(command); }
/// <summary> /// Publishes the provided <paramref name="event"/> on the event bus. /// </summary> public virtual void Publish <TEvent>(TEvent @event) where TEvent : IEvent <TAuthenticationToken> { Type eventType = @event.GetType(); string eventName = eventType.FullName; ISagaEvent <TAuthenticationToken> sagaEvent = @event as ISagaEvent <TAuthenticationToken>; if (sagaEvent != null) { eventName = string.Format("Cqrs.Events.SagaEvent[{0}]", sagaEvent.Event.GetType().FullName); } DateTimeOffset startedAt = DateTimeOffset.UtcNow; Stopwatch mainStopWatch = Stopwatch.StartNew(); string responseCode = "200"; bool wasSuccessfull = false; IDictionary <string, string> telemetryProperties = new Dictionary <string, string> { { "Type", "InProcessBus" } }; string telemetryName = string.Format("{0}/{1}/{2}", eventName, @event.GetIdentity(), @event.Id); var telemeteredEvent = @event as ITelemeteredMessage; if (telemeteredEvent != null) { telemetryName = telemeteredEvent.TelemetryName; } telemetryName = string.Format("Event/{0}", telemetryName); try { if (@event.Frameworks != null && @event.Frameworks.Contains("Built-In")) { Logger.LogInfo("The provided event has already been processed by the Built-In bus.", string.Format("{0}\\PrepareAndValidateEvent({1})", GetType().FullName, eventType.FullName)); return; } if (@event.AuthenticationToken == null || @event.AuthenticationToken.Equals(default(TAuthenticationToken))) { @event.AuthenticationToken = AuthenticationTokenHelper.GetAuthenticationToken(); } @event.CorrelationId = CorrelationIdHelper.GetCorrelationId(); if (string.IsNullOrWhiteSpace(@event.OriginatingFramework)) { @event.TimeStamp = DateTimeOffset.UtcNow; @event.OriginatingFramework = "Built-In"; } var frameworks = new List <string>(); if (@event.Frameworks != null) { frameworks.AddRange(@event.Frameworks); } frameworks.Add("Built-In"); @event.Frameworks = frameworks; bool isRequired; if (!ConfigurationManager.TryGetSetting(string.Format("{0}.IsRequired", eventName), out isRequired)) { isRequired = true; } IEnumerable <Action <IMessage> > handlers = Routes.GetHandlers(@event, isRequired).Select(x => x.Delegate).ToList(); // This check doesn't require an isRequired check as there will be an exception raised above and handled below. if (!handlers.Any()) { Logger.LogDebug(string.Format("An event handler for '{0}' is not required.", eventName)); } foreach (Action <IMessage> handler in handlers) { IList <IEvent <TAuthenticationToken> > events; if (EventWaits.TryGetValue(@event.CorrelationId, out events)) { events.Add(@event); } handler(@event); } Logger.LogInfo(string.Format("An event was sent of type {0}.", eventName)); wasSuccessfull = true; } catch (Exception exception) { responseCode = "500"; Logger.LogError("An issue occurred while trying to publish an event.", exception: exception, metaData: new Dictionary <string, object> { { "Event", @event } }); throw; } finally { mainStopWatch.Stop(); TelemetryHelper.TrackDependency("InProcessBus/EventBus", "Event", telemetryName, null, startedAt, mainStopWatch.Elapsed, responseCode, wasSuccessfull, telemetryProperties); } }
protected virtual void ReceiveEvent(BrokeredMessage message) { var brokeredMessageRenewCancellationTokenSource = new CancellationTokenSource(); try { Logger.LogDebug(string.Format("An event message arrived with the id '{0}'.", message.MessageId)); string messageBody = message.GetBody <string>(); IEvent <TAuthenticationToken> @event; try { @event = MessageSerialiser.DeserialiseEvent(messageBody); } catch (JsonSerializationException exception) { JsonSerializationException checkException = exception; bool safeToExit = false; do { if (checkException.Message.StartsWith("Could not load assembly")) { safeToExit = true; break; } } while ((checkException = checkException.InnerException as JsonSerializationException) != null); if (safeToExit) { const string pattern = @"(?<=^Error resolving type specified in JSON ').+?(?='\. Path '\$type')"; Match match = new Regex(pattern).Match(exception.Message); if (match.Success) { string[] typeParts = match.Value.Split(','); if (typeParts.Length == 2) { string classType = typeParts[0]; bool isRequired; if (!ConfigurationManager.TryGetSetting(string.Format("{0}.IsRequired", classType), out isRequired)) { isRequired = true; } if (!isRequired) { // Remove message from queue message.Complete(); Logger.LogDebug(string.Format("An event message arrived with the id '{0}' but processing was skipped due to event settings.", message.MessageId)); return; } } } } throw; } CorrelationIdHelper.SetCorrelationId(@event.CorrelationId); Logger.LogInfo(string.Format("An event message arrived with the id '{0}' was of type {1}.", message.MessageId, @event.GetType().FullName)); bool canRefresh; if (!ConfigurationManager.TryGetSetting(string.Format("{0}.ShouldRefresh", @event.GetType().FullName), out canRefresh)) { canRefresh = false; } if (canRefresh) { Task.Factory.StartNew(() => { while (!brokeredMessageRenewCancellationTokenSource.Token.IsCancellationRequested) { //Based on LockedUntilUtc property to determine if the lock expires soon if (DateTime.UtcNow > message.LockedUntilUtc.AddSeconds(-10)) { // If so, repeat the message message.RenewLock(); } Thread.Sleep(500); } }, brokeredMessageRenewCancellationTokenSource.Token); } ReceiveEvent(@event); // Remove message from queue message.Complete(); Logger.LogDebug(string.Format("An event message arrived and was processed with the id '{0}'.", message.MessageId)); } catch (Exception exception) { // Indicates a problem, unlock message in queue Logger.LogError(string.Format("An event message arrived with the id '{0}' but failed to be process.", message.MessageId), exception: exception); message.Abandon(); } finally { // Cancel the lock of renewing the task brokeredMessageRenewCancellationTokenSource.Cancel(); } }
protected virtual void ReceiveCommand(BrokeredMessage message) { try { Logger.LogDebug(string.Format("A command message arrived with the id '{0}'.", message.MessageId)); string messageBody = message.GetBody <string>(); ICommand <TAuthenticationToken> command; try { command = MessageSerialiser.DeserialiseCommand(messageBody); } catch (JsonSerializationException exception) { JsonSerializationException checkException = exception; bool safeToExit = false; do { if (checkException.Message.StartsWith("Could not load assembly")) { safeToExit = true; break; } } while ((checkException = checkException.InnerException as JsonSerializationException) != null); if (safeToExit) { const string pattern = @"(?<=^Error resolving type specified in JSON ').+?(?='\. Path '\$type')"; Match match = new Regex(pattern).Match(exception.Message); if (match.Success) { string[] typeParts = match.Value.Split(','); if (typeParts.Length == 2) { string classType = typeParts[0]; bool isRequired = BusHelper.IsEventRequired(classType); if (!isRequired) { // Remove message from queue message.Complete(); Logger.LogDebug(string.Format("A command message arrived with the id '{0}' but processing was skipped due to command settings.", message.MessageId)); return; } } } } throw; } CorrelationIdHelper.SetCorrelationId(command.CorrelationId); Logger.LogInfo(string.Format("A command message arrived with the id '{0}' was of type {1}.", message.MessageId, command.GetType().FullName)); ReceiveCommand(command); // Remove message from queue message.Complete(); Logger.LogDebug(string.Format("A command message arrived and was processed with the id '{0}'.", message.MessageId)); } catch (Exception exception) { // Indicates a problem, unlock message in queue Logger.LogError(string.Format("A command message arrived with the id '{0}' but failed to be process.", message.MessageId), exception: exception); message.Abandon(); } }
/// <summary> /// Create a <see cref="BrokeredMessage"/> with additional properties to aid routing and tracing /// </summary> protected virtual BrokeredMessage CreateBrokeredMessage <TMessage>(Func <TMessage, string> serialiserFunction, Type messageType, TMessage message) { string messageBody = serialiserFunction(message); var brokeredMessage = new BrokeredMessage(messageBody) { CorrelationId = CorrelationIdHelper.GetCorrelationId().ToString("N") }; brokeredMessage.Properties.Add("Type", messageType.FullName); brokeredMessage.Properties.Add("Source", string.Format("{0}/{1}/{2}/{3}", LoggerSettings.ModuleName, LoggerSettings.Instance, LoggerSettings.Environment, LoggerSettings.EnvironmentInstance)); // see https://github.com/Chinchilla-Software-Com/CQRS/wiki/Inter-process-function-security</remarks> string configurationKey = string.Format("{0}.SigningToken", messageType.FullName); string signingToken; if (!ConfigurationManager.TryGetSetting(configurationKey, out signingToken) || string.IsNullOrWhiteSpace(signingToken)) { if (!ConfigurationManager.TryGetSetting(SigningTokenConfigurationKey, out signingToken) || string.IsNullOrWhiteSpace(signingToken)) { signingToken = Guid.Empty.ToString("N"); } } if (!string.IsNullOrWhiteSpace(signingToken)) { using (var hashStream = new MemoryStream(Encoding.UTF8.GetBytes(string.Concat("{0}{1}", signingToken, messageBody)))) brokeredMessage.Properties.Add("Signature", Convert.ToBase64String(Signer.ComputeHash(hashStream))); } try { var stackTrace = new StackTrace(); StackFrame[] stackFrames = stackTrace.GetFrames(); if (stackFrames != null) { foreach (StackFrame frame in stackFrames) { MethodBase method = frame.GetMethod(); if (method.ReflectedType == null) { continue; } try { if (ExclusionNamespaces.All(@namespace => !method.ReflectedType.FullName.StartsWith(@namespace))) { brokeredMessage.Properties.Add("Source-Method", string.Format("{0}.{1}", method.ReflectedType.FullName, method.Name)); break; } } catch { // Just move on } } } } catch { // Just move on } return(brokeredMessage); }
protected virtual void DequeuAndProcessCommand(string queueName) { long loop = long.MinValue; while (true) { try { ConcurrentQueue <ICommand <TAuthenticationToken> > queue; if (QueueTracker.TryGetValue(queueName, out queue)) { while (!queue.IsEmpty) { ICommand <TAuthenticationToken> command; if (queue.TryDequeue(out command)) { try { CorrelationIdHelper.SetCorrelationId(command.CorrelationId); } catch (Exception exception) { Logger.LogError(string.Format("Trying to set the CorrelationId from the command type {1} for a request for the queue '{0}' failed.", queueName, command.GetType()), exception: exception); } try { AuthenticationTokenHelper.SetAuthenticationToken(command.AuthenticationToken); } catch (Exception exception) { Logger.LogError(string.Format("Trying to set the AuthenticationToken from the command type {1} for a request for the queue '{0}' failed.", queueName, command.GetType()), exception: exception); } try { ReceiveCommand(command); } catch (Exception exception) { Logger.LogError(string.Format("Processing the command type {1} for a request for the queue '{0}' failed.", queueName, command.GetType()), exception: exception); queue.Enqueue(command); } } else { Logger.LogDebug(string.Format("Trying to dequeue a command from the queue '{0}' failed.", queueName)); } } } else { Logger.LogDebug(string.Format("Trying to find the queue '{0}' failed.", queueName)); } if (loop++ % 5 == 0) { Thread.Yield(); } else { Thread.Sleep(100); } if (loop == long.MaxValue) { loop = long.MinValue; } } catch (Exception exception) { Logger.LogError(string.Format("Dequeuing and processing a request for the queue '{0}' failed.", queueName), exception: exception); } } }
/// <summary> /// Send out an event to specific user RSNs /// </summary> void INotificationHub.SendUsersEvent <TSingleSignOnToken>(IEvent <TSingleSignOnToken> eventData, params Guid[] userRsnCollection) { IList <Guid> optimisedUserRsnCollection = (userRsnCollection ?? Enumerable.Empty <Guid>()).ToList(); Logger.LogDebug(string.Format("Sending a message on the hub for user RSNs [{0}].", string.Join(", ", optimisedUserRsnCollection))); try { var tokenSource = new CancellationTokenSource(); Task.Factory.StartNewSafely ( () => { foreach (Guid userRsn in optimisedUserRsnCollection) { var metaData = GetAdditionalDataForLogging(userRsn); try { Clients .Group(string.Format("UserRsn-{0}", userRsn)) .notifyEvent(new { Type = eventData.GetType().FullName, Data = eventData, CorrelationId = CorrelationIdHelper.GetCorrelationId() }); } catch (TimeoutException exception) { Logger.LogWarning("Sending a message on the hub timed-out.", exception: exception, metaData: metaData); } catch (Exception exception) { Logger.LogError("Sending a message on the hub resulted in an error.", exception: exception, metaData: metaData); } } }, tokenSource.Token ); tokenSource.CancelAfter(15 * 1000); } catch (Exception exception) { foreach (Guid userRsn in optimisedUserRsnCollection) { Logger.LogError("Queueing a message on the hub resulted in an error.", exception: exception, metaData: GetAdditionalDataForLogging(userRsn)); } } }
public virtual IEvent <TAuthenticationToken> ReceiveEvent(string messageBody, Func <IEvent <TAuthenticationToken>, bool?> receiveEventHandler, string messageId, Action skippedAction = null, Action lockRefreshAction = null) { IEvent <TAuthenticationToken> @event; try { @event = MessageSerialiser.DeserialiseEvent(messageBody); } catch (JsonSerializationException exception) { JsonSerializationException checkException = exception; bool safeToExit = false; do { if (checkException.Message.StartsWith("Could not load assembly")) { safeToExit = true; break; } } while ((checkException = checkException.InnerException as JsonSerializationException) != null); if (safeToExit) { const string pattern = @"(?<=^Error resolving type specified in JSON ').+?(?='\. Path '\$type')"; Match match = new Regex(pattern).Match(exception.Message); if (match.Success) { string[] typeParts = match.Value.Split(','); if (typeParts.Length == 2) { string classType = typeParts[0]; bool isRequired = BusHelper.IsEventRequired(string.Format("{0}.IsRequired", classType)); if (!isRequired) { if (skippedAction != null) { skippedAction(); } return(null); } } } } throw; } string eventTypeName = @event.GetType().FullName; CorrelationIdHelper.SetCorrelationId(@event.CorrelationId); AuthenticationTokenHelper.SetAuthenticationToken(@event.AuthenticationToken); Logger.LogInfo(string.Format("An event message arrived with the {0} was of type {1}.", messageId, eventTypeName)); bool canRefresh; if (!ConfigurationManager.TryGetSetting(string.Format("{0}.ShouldRefresh", eventTypeName), out canRefresh)) { canRefresh = false; } if (canRefresh) { if (lockRefreshAction == null) { Logger.LogWarning(string.Format("An event message arrived with the {0} was of type {1} and was destined to support lock renewal, but no action was provided.", messageId, eventTypeName)); } else { lockRefreshAction(); } } // a false response means the action wasn't handled, but didn't throw an error, so we assume, by convention, that this means it was skipped. bool?result = receiveEventHandler(@event); if (result != null && !result.Value) { if (skippedAction != null) { skippedAction(); } } return(@event); }
/// <summary> /// Send out an event to specific user token /// </summary> void INotificationHub.SendUserEvent <TSingleSignOnToken>(IEvent <TSingleSignOnToken> eventData, string userToken) { Logger.LogDebug(string.Format("Sending a message on the hub for user [{0}].", userToken)); try { var tokenSource = new CancellationTokenSource(); Task.Factory.StartNewSafely ( () => { IDictionary <string, object> metaData = GetAdditionalDataForLogging(userToken); try { CurrentHub .Clients .Group(string.Format("User-{0}", userToken)) .notifyEvent(new { Type = eventData.GetType().FullName, Data = eventData, CorrelationId = CorrelationIdHelper.GetCorrelationId() }); } catch (TimeoutException exception) { Logger.LogWarning("Sending a message on the hub timed-out.", exception: exception, metaData: metaData); } catch (Exception exception) { Logger.LogError("Sending a message on the hub resulted in an error.", exception: exception, metaData: metaData); } }, tokenSource.Token ); tokenSource.CancelAfter(15 * 1000); } catch (Exception exception) { Logger.LogError("Queueing a message on the hub resulted in an error.", exception: exception, metaData: GetAdditionalDataForLogging(userToken)); } }
protected void DequeuAndProcessEvent(string queueName) { SpinWait.SpinUntil ( () => { try { ConcurrentQueue <IEvent <TAuthenticationToken> > queue; if (QueueTracker.TryGetValue(queueName, out queue)) { while (!queue.IsEmpty) { IEvent <TAuthenticationToken> @event; if (queue.TryDequeue(out @event)) { try { CorrelationIdHelper.SetCorrelationId(@event.CorrelationId); } catch (Exception exception) { Logger.LogError(string.Format("Trying to set the CorrelationId from the event type {1} for a request for the queue '{0}' failed.", queueName, @event.GetType()), exception: exception); } try { AuthenticationTokenHelper.SetAuthenticationToken(@event.AuthenticationToken); } catch (Exception exception) { Logger.LogError(string.Format("Trying to set the AuthenticationToken from the event type {1} for a request for the queue '{0}' failed.", queueName, @event.GetType()), exception: exception); } try { ReceiveEvent(@event); } catch (Exception exception) { Logger.LogError(string.Format("Processing the event type {1} for a request for the queue '{0}' failed.", queueName, @event.GetType()), exception: exception); queue.Enqueue(@event); } } else { Logger.LogDebug(string.Format("Trying to dequeue a event from the queue '{0}' failed.", queueName)); } } } else { Logger.LogDebug(string.Format("Trying to find the queue '{0}' failed.", queueName)); } Thread.Sleep(100); } catch (Exception exception) { Logger.LogError(string.Format("Dequeuing and processing a request for the queue '{0}' failed.", queueName), exception: exception); } // Always return false to keep this spinning. return(false); }, sleepInMilliseconds: 1000 ); }
/// <summary> /// Send out an event to all users /// </summary> void INotificationHub.SendAllUsersEvent <TSingleSignOnToken>(IEvent <TSingleSignOnToken> eventData) { Logger.LogDebug("Sending a message on the hub to all users."); try { var tokenSource = new CancellationTokenSource(); Task.Factory.StartNewSafely ( () => { try { CurrentHub .Clients .All .notifyEvent(new { Type = eventData.GetType().FullName, Data = eventData, CorrelationId = CorrelationIdHelper.GetCorrelationId() }); } catch (TimeoutException exception) { Logger.LogWarning("Sending a message on the hub to all users timed-out.", exception: exception); } catch (Exception exception) { Logger.LogError("Sending a message on the hub to all users resulted in an error.", exception: exception); } }, tokenSource.Token ); tokenSource.CancelAfter(15 * 1000); } catch (Exception exception) { Logger.LogError("Queueing a message on the hub to all users resulted in an error.", exception: exception); } }
/// <summary> /// Publishes the provided <paramref name="commands"/> on the command bus. /// </summary> public virtual void Publish <TCommand>(IEnumerable <TCommand> commands) where TCommand : ICommand <TAuthenticationToken> { IList <TCommand> sourceCommands = commands.ToList(); DateTimeOffset startedAt = DateTimeOffset.UtcNow; Stopwatch mainStopWatch = Stopwatch.StartNew(); string responseCode = "200"; bool wasSuccessfull = false; IDictionary <string, string> telemetryProperties = new Dictionary <string, string> { { "Type", "Azure/Servicebus" } }; string telemetryName = "Commands"; string telemetryNames = string.Empty; foreach (TCommand command in sourceCommands) { string subTelemetryName = string.Format("{0}/{1}", command.GetType().FullName, command.Id); var telemeteredCommand = command as ITelemeteredMessage; if (telemeteredCommand != null) { subTelemetryName = telemeteredCommand.TelemetryName; } telemetryNames = string.Format("{0}{1},", telemetryNames, subTelemetryName); } if (telemetryNames.Length > 0) { telemetryNames = telemetryNames.Substring(0, telemetryNames.Length - 1); } telemetryProperties.Add("Commands", telemetryNames); try { IList <string> sourceCommandMessages = new List <string>(); IList <BrokeredMessage> brokeredMessages = new List <BrokeredMessage>(sourceCommands.Count); foreach (TCommand command in sourceCommands) { if (!AzureBusHelper.PrepareAndValidateCommand(command, "Azure-ServiceBus")) { continue; } var brokeredMessage = new BrokeredMessage(MessageSerialiser.SerialiseCommand(command)) { CorrelationId = CorrelationIdHelper.GetCorrelationId().ToString("N") }; brokeredMessage.Properties.Add("Type", command.GetType().FullName); brokeredMessages.Add(brokeredMessage); sourceCommandMessages.Add(string.Format("A command was sent of type {0}.", command.GetType().FullName)); } try { PrivateServiceBusPublisher.SendBatch(brokeredMessages); } catch (QuotaExceededException exception) { responseCode = "429"; Logger.LogError("The size of the command being sent was too large.", exception: exception, metaData: new Dictionary <string, object> { { "Commands", sourceCommands } }); throw; } catch (Exception exception) { responseCode = "500"; Logger.LogError("An issue occurred while trying to publish a command.", exception: exception, metaData: new Dictionary <string, object> { { "Commands", sourceCommands } }); throw; } foreach (string message in sourceCommandMessages) { Logger.LogInfo(message); } wasSuccessfull = true; } finally { mainStopWatch.Stop(); TelemetryHelper.TrackDependency("Azure/Servicebus/CommandBus", "Command", telemetryName, null, startedAt, mainStopWatch.Elapsed, responseCode, wasSuccessfull, telemetryProperties); } }
/// <summary> /// Save and persist the provided <paramref name="aggregate"/>, optionally providing the version number the <see cref="IAggregateRoot{TAuthenticationToken}"/> is expected to be at. /// </summary> /// <typeparam name="TAggregateRoot">The <see cref="Type"/> of the <see cref="IAggregateRoot{TAuthenticationToken}"/>.</typeparam> /// <param name="aggregate">The <see cref="IAggregateRoot{TAuthenticationToken}"/> to save and persist.</param> /// <param name="expectedVersion">The version number the <see cref="IAggregateRoot{TAuthenticationToken}"/> is expected to be at.</param> public virtual void Save <TAggregateRoot>(TAggregateRoot aggregate, int?expectedVersion = null) where TAggregateRoot : IAggregateRoot <TAuthenticationToken> { IList <IEvent <TAuthenticationToken> > uncommittedChanges = aggregate.GetUncommittedChanges().ToList(); if (!uncommittedChanges.Any()) { return; } if (expectedVersion != null) { IEnumerable <IEvent <TAuthenticationToken> > eventStoreResults = EventStore.Get(aggregate.GetType(), aggregate.Id, false, expectedVersion.Value); if (eventStoreResults.Any()) { throw new ConcurrencyException(aggregate.Id); } } var eventsToPublish = new List <IEvent <TAuthenticationToken> >(); int i = 0; int version = aggregate.Version; foreach (IEvent <TAuthenticationToken> @event in uncommittedChanges) { var eventWithIdentity = @event as IEventWithIdentity <TAuthenticationToken>; if (eventWithIdentity != null) { if (eventWithIdentity.Rsn == Guid.Empty) { eventWithIdentity.Rsn = aggregate.Id; } if (eventWithIdentity.Rsn == Guid.Empty) { throw new AggregateOrEventMissingIdException(aggregate.GetType(), @event.GetType()); } } else { if (@event.Id == Guid.Empty) { @event.Id = aggregate.Id; } if (@event.Id == Guid.Empty) { throw new AggregateOrEventMissingIdException(aggregate.GetType(), @event.GetType()); } } i++; version++; @event.Version = version; @event.TimeStamp = DateTimeOffset.UtcNow; @event.CorrelationId = CorrelationIdHelper.GetCorrelationId(); EventStore.Save(aggregate.GetType(), @event); eventsToPublish.Add(@event); } aggregate.MarkChangesAsCommitted(); foreach (IEvent <TAuthenticationToken> @event in eventsToPublish) { PublishEvent(@event); } }
/// <summary> /// Publishes the provided <paramref name="command"/> on the command bus. /// </summary> public virtual void Publish <TCommand>(TCommand command) where TCommand : ICommand <TAuthenticationToken> { DateTimeOffset startedAt = DateTimeOffset.UtcNow; Stopwatch mainStopWatch = Stopwatch.StartNew(); string responseCode = "200"; bool mainWasSuccessfull = false; bool telemeterOverall = false; IDictionary <string, string> telemetryProperties = new Dictionary <string, string> { { "Type", "Azure/Servicebus" } }; string telemetryName = string.Format("{0}/{1}/{2}", command.GetType().FullName, command.GetIdentity(), command.Id); var telemeteredCommand = command as ITelemeteredMessage; if (telemeteredCommand != null) { telemetryName = telemeteredCommand.TelemetryName; } else { telemetryName = string.Format("Command/{0}", telemetryName); } try { if (!AzureBusHelper.PrepareAndValidateCommand(command, "Azure-ServiceBus")) { return; } Type commandType = typeof(TCommand); bool?isPublicBusRequired = BusHelper.IsPublicBusRequired(commandType); bool?isPrivateBusRequired = BusHelper.IsPrivateBusRequired(commandType); // We only add telemetry for overall operations if two occured telemeterOverall = isPublicBusRequired != null && isPublicBusRequired.Value && isPrivateBusRequired != null && isPrivateBusRequired.Value; // Backwards compatibility and simplicity bool wasSuccessfull; Stopwatch stopWatch = Stopwatch.StartNew(); if ((isPublicBusRequired == null || !isPublicBusRequired.Value) && (isPrivateBusRequired == null || !isPrivateBusRequired.Value)) { stopWatch.Restart(); responseCode = "200"; wasSuccessfull = false; try { var brokeredMessage = new BrokeredMessage(MessageSerialiser.SerialiseCommand(command)) { CorrelationId = CorrelationIdHelper.GetCorrelationId().ToString("N") }; brokeredMessage.Properties.Add("Type", commandType.GetType().FullName); PublicServiceBusPublisher.Send(brokeredMessage); wasSuccessfull = true; } catch (QuotaExceededException exception) { responseCode = "429"; Logger.LogError("The size of the command being sent was too large or the topic has reached it's limit.", exception: exception, metaData: new Dictionary <string, object> { { "Command", command } }); throw; } catch (Exception exception) { responseCode = "500"; Logger.LogError("An issue occurred while trying to publish a command.", exception: exception, metaData: new Dictionary <string, object> { { "Command", command } }); throw; } finally { TelemetryHelper.TrackDependency("Azure/Servicebus/EventBus", "Command", telemetryName, "Default Bus", startedAt, stopWatch.Elapsed, responseCode, wasSuccessfull, telemetryProperties); } Logger.LogDebug(string.Format("An command was published on the public bus with the id '{0}' was of type {1}.", command.Id, command.GetType().FullName)); } if ((isPublicBusRequired != null && isPublicBusRequired.Value)) { stopWatch.Restart(); responseCode = "200"; wasSuccessfull = false; try { var brokeredMessage = new BrokeredMessage(MessageSerialiser.SerialiseCommand(command)) { CorrelationId = CorrelationIdHelper.GetCorrelationId().ToString("N") }; brokeredMessage.Properties.Add("Type", commandType.GetType().FullName); PublicServiceBusPublisher.Send(brokeredMessage); wasSuccessfull = true; } catch (QuotaExceededException exception) { responseCode = "429"; Logger.LogError("The size of the command being sent was too large or the topic has reached it's limit.", exception: exception, metaData: new Dictionary <string, object> { { "Command", command } }); throw; } catch (Exception exception) { responseCode = "500"; Logger.LogError("An issue occurred while trying to publish a command.", exception: exception, metaData: new Dictionary <string, object> { { "Command", command } }); throw; } finally { TelemetryHelper.TrackDependency("Azure/Servicebus/EventBus", "Command", telemetryName, "Public Bus", startedAt, stopWatch.Elapsed, responseCode, wasSuccessfull, telemetryProperties); } Logger.LogDebug(string.Format("An command was published on the public bus with the id '{0}' was of type {1}.", command.Id, command.GetType().FullName)); } if (isPrivateBusRequired != null && isPrivateBusRequired.Value) { stopWatch.Restart(); responseCode = "200"; wasSuccessfull = false; try { var brokeredMessage = new BrokeredMessage(MessageSerialiser.SerialiseCommand(command)) { CorrelationId = CorrelationIdHelper.GetCorrelationId().ToString("N") }; brokeredMessage.Properties.Add("Type", command.GetType().FullName); PrivateServiceBusPublisher.Send(brokeredMessage); wasSuccessfull = true; } catch (QuotaExceededException exception) { responseCode = "429"; Logger.LogError("The size of the command being sent was too large or the topic has reached it's limit.", exception: exception, metaData: new Dictionary <string, object> { { "Command", command } }); throw; } catch (Exception exception) { responseCode = "500"; Logger.LogError("An issue occurred while trying to publish an command.", exception: exception, metaData: new Dictionary <string, object> { { "Command", command } }); throw; } finally { TelemetryHelper.TrackDependency("Azure/Servicebus/EventBus", "Command", telemetryName, "Private Bus", startedAt, stopWatch.Elapsed, responseCode, wasSuccessfull, telemetryProperties); } Logger.LogDebug(string.Format("An command was published on the private bus with the id '{0}' was of type {1}.", command.Id, command.GetType().FullName)); } mainWasSuccessfull = true; } finally { mainStopWatch.Stop(); if (telemeterOverall) { TelemetryHelper.TrackDependency("Azure/Servicebus/CommandBus", "Command", telemetryName, null, startedAt, mainStopWatch.Elapsed, responseCode, mainWasSuccessfull, telemetryProperties); } } }
/// <summary> /// Publishes the provided <paramref name="event"/> on the event bus. /// </summary> public virtual void Publish <TEvent>(TEvent @event) where TEvent : IEvent <TAuthenticationToken> { if (@event == null) { Logger.LogDebug("No event to publish."); return; } Type eventType = @event.GetType(); DateTimeOffset startedAt = DateTimeOffset.UtcNow; Stopwatch mainStopWatch = Stopwatch.StartNew(); string responseCode = null; bool mainWasSuccessfull = false; bool telemeterOverall = false; IDictionary <string, string> telemetryProperties = new Dictionary <string, string> { { "Type", "Azure/Servicebus" } }; string telemetryName = string.Format("{0}/{1}/{2}", eventType.FullName, @event.GetIdentity(), @event.Id); var telemeteredEvent = @event as ITelemeteredMessage; if (telemeteredEvent != null) { telemetryName = telemeteredEvent.TelemetryName; } else { telemetryName = string.Format("Event/{0}", telemetryName); } try { if (!AzureBusHelper.PrepareAndValidateEvent(@event, "Azure-ServiceBus")) { return; } bool?isPublicBusRequired = BusHelper.IsPublicBusRequired(eventType); bool?isPrivateBusRequired = BusHelper.IsPrivateBusRequired(eventType); // We only add telemetry for overall operations if two occured telemeterOverall = isPublicBusRequired != null && isPublicBusRequired.Value && isPrivateBusRequired != null && isPrivateBusRequired.Value; // Backwards compatibility and simplicity bool wasSuccessfull; Stopwatch stopWatch = Stopwatch.StartNew(); if ((isPublicBusRequired == null || !isPublicBusRequired.Value) && (isPrivateBusRequired == null || !isPrivateBusRequired.Value)) { stopWatch.Restart(); responseCode = "200"; wasSuccessfull = false; try { var brokeredMessage = new BrokeredMessage(MessageSerialiser.SerialiseEvent(@event)) { CorrelationId = CorrelationIdHelper.GetCorrelationId().ToString("N") }; brokeredMessage.Properties.Add("Type", eventType.FullName); brokeredMessage.Properties.Add("Source", string.Format("{0}/{1}/{2}/{3}", LoggerSettings.ModuleName, LoggerSettings.Instance, LoggerSettings.Environment, LoggerSettings.EnvironmentInstance)); int count = 1; do { try { PublicServiceBusPublisher.Send(brokeredMessage); break; } catch (TimeoutException) { if (count >= TimeoutOnSendRetryMaximumCount) { throw; } } count++; } while (true); wasSuccessfull = true; } catch (QuotaExceededException exception) { responseCode = "429"; Logger.LogError("The size of the event being sent was too large or the topic has reached it's limit.", exception: exception, metaData: new Dictionary <string, object> { { "Event", @event } }); throw; } catch (Exception exception) { responseCode = "500"; Logger.LogError("An issue occurred while trying to publish an event.", exception: exception, metaData: new Dictionary <string, object> { { "Event", @event } }); throw; } finally { TelemetryHelper.TrackDependency("Azure/Servicebus/EventBus", "Event", telemetryName, "Default Bus", startedAt, stopWatch.Elapsed, responseCode, wasSuccessfull, telemetryProperties); } Logger.LogDebug(string.Format("An event was published on the public bus with the id '{0}' was of type {1}.", @event.Id, eventType.FullName)); } if ((isPublicBusRequired != null && isPublicBusRequired.Value)) { stopWatch.Restart(); responseCode = "200"; wasSuccessfull = false; try { var brokeredMessage = new BrokeredMessage(MessageSerialiser.SerialiseEvent(@event)) { CorrelationId = CorrelationIdHelper.GetCorrelationId().ToString("N") }; brokeredMessage.Properties.Add("Type", eventType.FullName); brokeredMessage.Properties.Add("Source", string.Format("{0}/{1}/{2}/{3}", LoggerSettings.ModuleName, LoggerSettings.Instance, LoggerSettings.Environment, LoggerSettings.EnvironmentInstance)); int count = 1; do { try { PublicServiceBusPublisher.Send(brokeredMessage); break; } catch (TimeoutException) { if (count >= TimeoutOnSendRetryMaximumCount) { throw; } } count++; } while (true); wasSuccessfull = true; } catch (QuotaExceededException exception) { responseCode = "429"; Logger.LogError("The size of the event being sent was too large.", exception: exception, metaData: new Dictionary <string, object> { { "Event", @event } }); throw; } catch (Exception exception) { responseCode = "500"; Logger.LogError("An issue occurred while trying to publish an event.", exception: exception, metaData: new Dictionary <string, object> { { "Event", @event } }); throw; } finally { TelemetryHelper.TrackDependency("Azure/Servicebus/EventBus", "Event", telemetryName, "Public Bus", startedAt, stopWatch.Elapsed, responseCode, wasSuccessfull, telemetryProperties); } Logger.LogDebug(string.Format("An event was published on the public bus with the id '{0}' was of type {1}.", @event.Id, eventType.FullName)); } if (isPrivateBusRequired != null && isPrivateBusRequired.Value) { stopWatch.Restart(); responseCode = "200"; wasSuccessfull = false; try { var brokeredMessage = new BrokeredMessage(MessageSerialiser.SerialiseEvent(@event)) { CorrelationId = CorrelationIdHelper.GetCorrelationId().ToString("N") }; brokeredMessage.Properties.Add("Type", eventType.FullName); brokeredMessage.Properties.Add("Source", string.Format("{0}/{1}/{2}/{3}", LoggerSettings.ModuleName, LoggerSettings.Instance, LoggerSettings.Environment, LoggerSettings.EnvironmentInstance)); int count = 1; do { try { PrivateServiceBusPublisher.Send(brokeredMessage); break; } catch (TimeoutException) { if (count >= TimeoutOnSendRetryMaximumCount) { throw; } } count++; } while (true); wasSuccessfull = true; } catch (QuotaExceededException exception) { responseCode = "429"; Logger.LogError("The size of the event being sent was too large.", exception: exception, metaData: new Dictionary <string, object> { { "Event", @event } }); throw; } catch (Exception exception) { responseCode = "500"; Logger.LogError("An issue occurred while trying to publish an event.", exception: exception, metaData: new Dictionary <string, object> { { "Event", @event } }); throw; } finally { TelemetryHelper.TrackDependency("Azure/Servicebus/EventBus", "Event", telemetryName, "Private Bus", startedAt, stopWatch.Elapsed, responseCode, wasSuccessfull, telemetryProperties); } Logger.LogDebug(string.Format("An event was published on the private bus with the id '{0}' was of type {1}.", @event.Id, eventType.FullName)); } mainWasSuccessfull = true; } finally { mainStopWatch.Stop(); if (telemeterOverall) { TelemetryHelper.TrackDependency("Azure/Servicebus/EventBus", "Event", telemetryName, null, startedAt, mainStopWatch.Elapsed, responseCode, mainWasSuccessfull, telemetryProperties); } } }