/// <summary> /// Reads the messages for the observer actor from its messagebox. /// </summary> /// <returns>The messages for the current observer actor.</returns> public virtual async Task <IEnumerable <Message> > ReadMessagesFromMessageBoxAsync() { EntityId id = await this.GetEntityIdAsync(); IEnumerable <Message> messages = await ProcessingHelper.ReadMessagesFromMessageBoxAsync(id.EntityUri); if (messages == null) { return(null); } StringBuilder stringBuilder = new StringBuilder($"Messages read from the MessageBox.\r\n[Observer]: {id}"); int i = 1; foreach (Message message in messages) { stringBuilder.Append($"\r\nMessage[{i++}]=[{message.Body}]"); } ServiceEventSource.Current.Message(stringBuilder.ToString()); return(messages); }
/// <summary> /// Unregisters an observable. This method is invoked by an observable. /// </summary> /// <param name="topic">The topic.</param> /// <param name="entityId">The entity id of the observable.</param> /// <param name="observers"> /// A list of observers in the same cluster node. This field is optional. /// When the list if not null or empty, the observer will forward the message to each observer which EntityId is in the /// list. /// </param> /// <returns>The asynchronous result of the operation.</returns> public async Task UnregisterObservableAsync(string topic, EntityId entityId, IEnumerable <EntityId> observers) { EntityId id = await this.GetEntityIdAsync(); if (id == null) { return; } if (string.IsNullOrWhiteSpace(topic)) { throw new ArgumentException($"The {nameof(topic)} parameter cannot be null.", nameof(topic)); } if (entityId == null) { throw new ArgumentException($"The {nameof(entityId)} parameter cannot be null.", nameof(entityId)); } for (int k = 1; k <= ConfigurationHelper.MaxQueryRetryCount; k++) { try { ConditionalValue <Dictionary <Uri, EntityId> > topicState = await this.StateManager.TryGetStateAsync <Dictionary <Uri, EntityId> >(topic); if (!topicState.HasValue) { ActorEventSource.Current.Message( $"Observer not registered to the specified topic.\r\n[Observable]: {entityId}\r\n[Observer]: {id}\r\n[Publication]: Topic=[{topic}]"); return; } Dictionary <Uri, EntityId> observableDictionary = topicState.Value; if (!observableDictionary.ContainsKey(entityId.EntityUri)) { ActorEventSource.Current.Message( $"Observer not registered to the specified observable.\r\n[Observable]: {entityId}\r\n[Observer]: {id}\r\n[Publication]: Topic=[{topic}]"); return; } observableDictionary.Remove(entityId.EntityUri); if (!observableDictionary.Any()) { await this.StateManager.TryRemoveStateAsync(topic); } else { await this.StateManager.SetStateAsync(topic, observableDictionary); } ActorEventSource.Current.Message( $"Observable successfully unregistered.\r\n[Observable]: {entityId}\r\n[Observer]: {id}\r\n[Publication]: Topic=[{topic}]"); break; } catch (FabricTransientException ex) { ActorEventSource.Current.Error(ex); if (k == ConfigurationHelper.MaxQueryRetryCount) { throw new TimeoutException(Constants.RetryTimeoutExhausted); } } catch (AggregateException ex) { foreach (Exception e in ex.InnerExceptions) { ActorEventSource.Current.Error(e); } throw; } catch (Exception ex) { ActorEventSource.Current.Error(ex); throw; } await Task.Delay(ConfigurationHelper.BackoffQueryDelay); } try { if (observers != null) { IList <EntityId> observerList = observers as IList <EntityId> ?? observers.ToList(); if (observerList.Any()) { StringBuilder builder = new StringBuilder($"Observer Proxy:\r\n[From]: {id}"); foreach (EntityId item in observerList) { builder.Append($"\r\n[To]: {item}"); } ActorEventSource.Current.Message(builder.ToString()); List <Task> taskList = new List <Task>(); taskList.AddRange(observerList.Select(observer => ProcessingHelper.UnregisterObservableAsync(topic, observer, entityId, null))); await Task.WhenAll(taskList.ToArray()); } } } catch (AggregateException ex) { foreach (Exception e in ex.InnerExceptions) { ActorEventSource.Current.Error(e); } throw; } catch (Exception ex) { ActorEventSource.Current.Error(ex); throw; } if (this.ObservableUnregistered == null) { return; } try { Delegate[] invocationList = this.ObservableUnregistered.GetInvocationList(); Task[] handlerTasks = new Task[invocationList.Length]; SubscriptionEventArgs args = new SubscriptionEventArgs(topic, entityId); for (int i = 0; i < invocationList.Length; i++) { handlerTasks[i] = ProcessingHelper.ExecuteEventHandlerAsync((Func <SubscriptionEventArgs, Task>)invocationList[i], args); } await Task.WhenAll(handlerTasks); } catch (AggregateException ex) { foreach (Exception e in ex.InnerExceptions) { ActorEventSource.Current.Error(e); } } catch (Exception ex) { ActorEventSource.Current.Error(ex); } }
/// <summary> /// Provides the observer with new data. This method is invoked by an observable. /// </summary> /// <param name="topic">The topic.</param> /// <param name="message">The current notification information.</param> /// <param name="entityId">The entity id of the observable.</param> /// <param name="observers"> /// A list of observers in the same cluster node. This field is optional. /// When the list if not null or empty, the observer will forward the message to each observer which EntityId is in the /// list. /// </param> /// <returns>The asynchronous result of the operation.</returns> public virtual async Task NotifyObserverAsync(string topic, Message message, EntityId entityId, IEnumerable <EntityId> observers) { try { EntityId id = await this.GetEntityIdAsync(); if (id == null) { return; } if (entityId == null) { entityId = new EntityId { ActorId = ActorId.CreateRandom(), ServiceUri = this.ServiceUri } } ; ActorEventSource.Current.Message( $"Message Received.\r\n[Observable]: {entityId}\r\n[Observer]: {id}\r\n[Message]: Topic=[{topic}] Body=[{message?.Body ?? "NULL"}]"); if (observers != null) { IList <EntityId> observerList = observers as IList <EntityId> ?? observers.ToList(); if (observerList.Any()) { StringBuilder builder = new StringBuilder($"Observer Proxy:\r\n[From]: {id}"); foreach (EntityId item in observerList) { builder.Append($"\r\n[To]: {item}"); } ActorEventSource.Current.Message(builder.ToString()); List <Task> taskList = new List <Task>(); taskList.AddRange( observerList.Select( observer => ProcessingHelper.NotifyObserverAsync( topic, message, observer, entityId, null))); await Task.WhenAll(taskList.ToArray()); } } } catch (AggregateException ex) { foreach (Exception e in ex.InnerExceptions) { ActorEventSource.Current.Error(e); } throw; } catch (Exception ex) { ActorEventSource.Current.Error(ex); throw; } if (this.NotificationMessageReceived == null) { return; } try { Delegate[] invocationList = this.NotificationMessageReceived.GetInvocationList(); Task[] handlerTasks = new Task[invocationList.Length]; NotificationEventArgs <Message> args = new NotificationEventArgs <Message>(topic, message, entityId); for (int i = 0; i < invocationList.Length; i++) { handlerTasks[i] = ProcessingHelper.ExecuteEventHandlerAsync((Func <NotificationEventArgs <Message>, Task>)invocationList[i], args); } await Task.WhenAll(handlerTasks); } catch (AggregateException ex) { foreach (Exception e in ex.InnerExceptions) { ActorEventSource.Current.Error(e); } } catch (Exception ex) { ActorEventSource.Current.Error(ex); } }
/// <summary> /// Provides the observer with new data. This method is invoked by an observable. /// </summary> /// <param name="topic">The topic.</param> /// <param name="message">The current notification information.</param> /// <param name="entityId">The entity id of the observable.</param> /// <param name="observers">A list of observers in the same cluster node. This field is optional. /// When the list if not null or empty, the observer will forward the message to each observer which EntityId is in the list.</param> /// <returns>The asynchronous result of the operation.</returns> public async Task NotifyObserverAsync(string topic, Message message, EntityId entityId, IEnumerable <EntityId> observers) { EntityId id = null; for (int k = 1; k <= ConfigurationHelper.MaxQueryRetryCount; k++) { try { id = await this.GetEntityIdAsync(); break; } catch (FabricTransientException ex) { ServiceEventSource.Current.Error(ex); if (k == ConfigurationHelper.MaxQueryRetryCount) { throw new TimeoutException(Constants.RetryTimeoutExhausted); } } catch (AggregateException ex) { foreach (Exception e in ex.InnerExceptions) { ServiceEventSource.Current.Error(e); } throw; } catch (Exception ex) { ServiceEventSource.Current.Error(ex); throw; } await Task.Delay(ConfigurationHelper.BackoffQueryDelay); } try { ServiceEventSource.Current.Message( $"Message Received.\r\n[Observable]: {entityId}\r\n[Observer]: {id}\r\n[Message]: Topic=[{topic}] Body=[{message?.Body ?? "NULL"}]"); if (observers != null) { IList <EntityId> observerList = observers as IList <EntityId> ?? observers.ToList(); if (observerList.Any()) { StringBuilder builder = new StringBuilder($"Observer Proxy:\r\n[From]: {id}"); foreach (EntityId item in observerList) { builder.Append($"\r\n[To]: {item}"); } ServiceEventSource.Current.Message(builder.ToString()); List <Task> taskList = new List <Task>(); taskList.AddRange( observerList.Select( observer => ProcessingHelper.NotifyObserverAsync( topic, message, observer, entityId, null))); await Task.WhenAll(taskList.ToArray()); } } } catch (AggregateException ex) { foreach (Exception e in ex.InnerExceptions) { ServiceEventSource.Current.Error(e); } throw; } catch (Exception ex) { ServiceEventSource.Current.Error(ex); throw; } if (this.NotificationMessageReceived == null) { return; } try { Delegate[] invocationList = this.NotificationMessageReceived.GetInvocationList(); Task[] handlerTasks = new Task[invocationList.Length]; NotificationEventArgs <Message> args = new NotificationEventArgs <Message>(topic, message, entityId); for (int i = 0; i < invocationList.Length; i++) { handlerTasks[i] = ProcessingHelper.ExecuteEventHandlerAsync((Func <NotificationEventArgs <Message>, Task>)invocationList[i], args); } await Task.WhenAll(handlerTasks); } catch (AggregateException ex) { foreach (Exception e in ex.InnerExceptions) { ServiceEventSource.Current.Error(e); } } catch (Exception ex) { ServiceEventSource.Current.Error(ex); } }
/// <summary> /// Sends data to observers for a given topic. /// </summary> /// <param name="topic">The topic.</param> /// <param name="message">The current notification information.</param> /// <param name="useObserverAsProxy">Observable uses one observer for each cluster node as a proxy when true, /// it directly sends the message to all observers otherwise.</param> /// <returns>The asynchronous result of the operation.</returns> public async Task NotifyObserversAsync(string topic, Message message, bool useObserverAsProxy) { EntityId id = await this.GetEntityIdAsync(); if (id == null) { return; } if (string.IsNullOrWhiteSpace(topic)) { throw new ArgumentException($"The {nameof(topic)} parameter cannot be null.", nameof(topic)); } ConditionalValue <Dictionary <Uri, ObserverInfo> > topicState = await this.StateManager.TryGetStateAsync <Dictionary <Uri, ObserverInfo> >(topic); if (!topicState.HasValue) { throw new ArgumentException($"{id} is not an observable for Topic=[{topic}]"); } Dictionary <Uri, ObserverInfo> observerDictionary = topicState.Value; if (string.IsNullOrWhiteSpace(message?.Body)) { throw new ArgumentException($"The {nameof(message)} parameter cannot be null.", nameof(message)); } try { List <Task> taskList = new List <Task>(); if (useObserverAsProxy) { if (JsonSerializerHelper.IsJson(message.Body)) { // Create the list of observers: an observer is added to the list only at least of of its // filter predicates is satisified by the message. JObject jObject = JsonSerializerHelper.Deserialize(message.Body); IEnumerable <EntityId> observerEnumerable = (from subscriptionInfo in observerDictionary. Where(kvp => kvp.Value.Predicates.Any()). Select(observer => observer.Value) let ok = subscriptionInfo.Predicates.Any(predicate => predicate(jObject)) where ok select subscriptionInfo.EntityId); // observers are grouped by NodeName taskList.AddRange( observerEnumerable. GroupBy(e => e.NodeName). Select(groupingByNodeName => ProcessingHelper.GetObserverProxyAndList(groupingByNodeName, true)). Select(tuple => ProcessingHelper.NotifyObserverAsync(topic, message, tuple.Item1, id, tuple.Item2))); } else { // observers are grouped by NodeName taskList.AddRange( observerDictionary. Select(kvp => kvp.Value.EntityId). GroupBy(e => e.NodeName). Select(groupingByNodeName => ProcessingHelper.GetObserverProxyAndList(groupingByNodeName, true)). Select(tuple => ProcessingHelper.NotifyObserverAsync(topic, message, tuple.Item1, id, tuple.Item2))); } } else { if (JsonSerializerHelper.IsJson(message.Body)) { JObject jObject = JsonSerializerHelper.Deserialize(message.Body); taskList.AddRange( (from subscriptionInfo in observerDictionary. Where(kvp => kvp.Value.Predicates.Any()). Select(observer => observer.Value) let ok = subscriptionInfo.Predicates.Any(predicate => predicate(jObject)) where ok select subscriptionInfo.EntityId). Select( entityId => ProcessingHelper.NotifyObserverAsync( topic, message, entityId, id, null))); } else { taskList.AddRange( observerDictionary.Select( observer => ProcessingHelper.NotifyObserverAsync( topic, message, observer.Value.EntityId, id, null))); } } await Task.WhenAll(taskList.ToArray()); } catch (AggregateException ex) { foreach (Exception e in ex.InnerExceptions) { ActorEventSource.Current.Error(e); } throw; } catch (Exception ex) { ActorEventSource.Current.Error(ex); throw; } }
/// <summary> /// Unregisters an entity as observable for a given topic. /// This method is called by a management service or actor. /// </summary> /// <param name="topic">The topic.</param> /// <param name="useObserverAsProxy">Observable uses one observer for each cluster node as a proxy when true, /// it directly sends the message to all observers otherwise.</param> /// <returns>The asynchronous result of the operation.</returns> public async Task UnregisterObservableActorAsync(string topic, bool useObserverAsProxy) { EntityId id = await this.GetEntityIdAsync(); if (id == null) { return; } if (string.IsNullOrWhiteSpace(topic)) { throw new ArgumentException($"The {nameof(topic)} parameter cannot be null.", nameof(topic)); } ConditionalValue <Dictionary <Uri, ObserverInfo> > topicState = await this.StateManager.TryGetStateAsync <Dictionary <Uri, ObserverInfo> >(topic); if (!topicState.HasValue) { throw new ArgumentException($"{id} is not an observable for Topic=[{topic}]"); } Dictionary <Uri, ObserverInfo> observerDictionary = topicState.Value; try { for (int k = 1; k <= ConfigurationHelper.MaxQueryRetryCount; k++) { try { IRegistryService registryService = ServiceProxy.Create <IRegistryService>(ConfigurationHelper.RegistryServiceUri, new ServicePartitionKey(PartitionResolver.Resolve(topic, ConfigurationHelper.RegistryServicePartitionCount))); await registryService.UnregisterObservableAsync(topic, id); break; } catch (FabricTransientException ex) { ActorEventSource.Current.Error(ex); if (k == ConfigurationHelper.MaxQueryRetryCount) { throw; } } catch (AggregateException ex) { foreach (Exception innerException in ex.InnerExceptions) { ActorEventSource.Current.Error(innerException); } if (k == ConfigurationHelper.MaxQueryRetryCount) { throw; } } catch (Exception ex) { ActorEventSource.Current.Error(ex); if (k == ConfigurationHelper.MaxQueryRetryCount) { throw; } } await Task.Delay(ConfigurationHelper.BackoffQueryDelay); } List <Task> taskList = new List <Task>(); try { if (useObserverAsProxy) { // observers are grouped by NodeName taskList.AddRange( observerDictionary. Select(kvp => kvp.Value.EntityId). GroupBy(e => e.NodeName). Select(groupingByNodeName => ProcessingHelper.GetObserverProxyAndList(groupingByNodeName, true)). Select(tuple => ProcessingHelper.UnregisterObservableAsync(topic, tuple.Item1, id, tuple.Item2))); } else { taskList.AddRange( observerDictionary.Select( observer => ProcessingHelper.UnregisterObservableAsync(topic, observer.Value.EntityId, id, null))); } await Task.WhenAll(taskList.ToArray()); } catch (AggregateException ex) { foreach (Exception e in ex.InnerExceptions) { ActorEventSource.Current.Error(e); } } catch (Exception ex) { ActorEventSource.Current.Error(ex); } await this.StateManager.TryRemoveStateAsync(topic); ActorEventSource.Current.Message($"Observable successfully unregistered.\r\n[Observable]: {id}\r\n[Publication]: Topic=[{topic}]."); } catch (FabricTransientException ex) { ActorEventSource.Current.Error(ex); } catch (AggregateException ex) { foreach (Exception e in ex.InnerExceptions) { ActorEventSource.Current.Error(e); } throw; } catch (Exception ex) { ActorEventSource.Current.Error(ex); throw; } }
/// <summary> /// Unregisters an observer. This methods is invoked by an observer. /// </summary> /// <param name="topic">The topic.</param> /// <param name="entityId">The entity id of the observer.</param> /// <returns>The asynchronous result of the operation.</returns> public async Task UnregisterObserverAsync(string topic, EntityId entityId) { EntityId id = await this.GetEntityIdAsync(); if (id == null) { return; } if (string.IsNullOrWhiteSpace(topic)) { throw new ArgumentException($"The {nameof(topic)} parameter cannot be null.", nameof(topic)); } if (entityId == null) { throw new ArgumentException($"The {nameof(entityId)} parameter cannot be null.", nameof(entityId)); } for (int k = 1; k <= ConfigurationHelper.MaxQueryRetryCount; k++) { try { ConditionalValue <Dictionary <Uri, ObserverInfo> > topicState = await this.StateManager.TryGetStateAsync <Dictionary <Uri, ObserverInfo> >(topic); if (!topicState.HasValue) { return; } Dictionary <Uri, ObserverInfo> observerDictionary = topicState.Value; if (!observerDictionary.ContainsKey(entityId.EntityUri)) { return; } observerDictionary.Remove(entityId.EntityUri); await this.StateManager.SetStateAsync(topic, observerDictionary); ActorEventSource.Current.Message($"Observer successfully unregistered.\r\n[Observable]: {id}\r\n[Observer]: {entityId}\r\n[Subscription]: Topic=[{topic}]"); break; } catch (FabricTransientException ex) { ActorEventSource.Current.Error(ex); if (k == ConfigurationHelper.MaxQueryRetryCount) { throw new TimeoutException(Constants.RetryTimeoutExhausted); } } catch (AggregateException ex) { foreach (Exception e in ex.InnerExceptions) { ActorEventSource.Current.Error(e); } throw; } catch (Exception ex) { ActorEventSource.Current.Error(ex); throw; } await Task.Delay(ConfigurationHelper.BackoffQueryDelay); } if (this.ObserverUnregistered == null) { return; } try { Delegate[] invocationList = this.ObserverUnregistered.GetInvocationList(); Task[] handlerTasks = new Task[invocationList.Length]; SubscriptionEventArgs args = new SubscriptionEventArgs(topic, entityId); for (int i = 0; i < invocationList.Length; i++) { handlerTasks[i] = ProcessingHelper.ExecuteEventHandlerAsync((Func <SubscriptionEventArgs, Task>)invocationList[i], args); } await Task.WhenAll(handlerTasks); } catch (AggregateException ex) { foreach (Exception e in ex.InnerExceptions) { ActorEventSource.Current.Error(e); } } catch (Exception ex) { ActorEventSource.Current.Error(ex); } }
/// <summary> /// Registers an observer. This methods is invoked by an observer. /// </summary> /// <param name="filterExpressions">Specifies filter expressions.</param> /// <param name="topic">The topic.</param> /// <param name="entityId">The entity id of the observer.</param> /// <returns>The asynchronous result of the operation.</returns> public async Task RegisterObserverAsync(string topic, IEnumerable <string> filterExpressions, EntityId entityId) { EntityId id = await this.GetEntityIdAsync(); if (id == null) { return; } if (string.IsNullOrWhiteSpace(topic)) { throw new ArgumentException($"The {nameof(topic)} parameter cannot be null.", nameof(topic)); } ConditionalValue <Dictionary <Uri, ObserverInfo> > topicState = await this.StateManager.TryGetStateAsync <Dictionary <Uri, ObserverInfo> >(topic); if (!topicState.HasValue) { throw new ArgumentException($"{id} is not an observable for Topic=[{topic}]"); } Dictionary <Uri, ObserverInfo> observerDictionary = topicState.Value; if (entityId == null) { throw new ArgumentException($"The {nameof(entityId)} parameter cannot be null.", nameof(entityId)); } IList <string> enumerable = filterExpressions as IList <string> ?? filterExpressions.ToList(); for (int k = 1; k <= ConfigurationHelper.MaxQueryRetryCount; k++) { try { if (!observerDictionary.ContainsKey(entityId.EntityUri)) { observerDictionary.Add(entityId.EntityUri, new ObserverInfo(enumerable, entityId)); StringBuilder stringBuilder = new StringBuilder($"Observer successfully registered.\r\n[Observable]: {id}\r\n[Observer]: {entityId}\r\n[Subscription]: Topic=[{topic}]"); int i = 1; foreach (string expression in enumerable.Where(expression => !string.IsNullOrWhiteSpace(expression))) { stringBuilder.Append($" FilterExpression[{i++}]=[{expression}]"); } await this.StateManager.SetStateAsync(topic, observerDictionary); ActorEventSource.Current.Message(stringBuilder.ToString()); } else { StringBuilder stringBuilder = new StringBuilder($"Observer already registered.\r\n[Observable]: {id}\r\n[Observer]: {entityId}\r\n[Subscription]: Topic=[{topic}]"); int i = 1; foreach (string expression in enumerable.Where(expression => !string.IsNullOrWhiteSpace(expression))) { stringBuilder.Append($" FilterExpression[{i++}]=[{expression}]"); } ActorEventSource.Current.Message(stringBuilder.ToString()); } break; } catch (FabricTransientException ex) { ActorEventSource.Current.Error(ex); if (k == ConfigurationHelper.MaxQueryRetryCount) { throw new TimeoutException(Constants.RetryTimeoutExhausted); } } catch (AggregateException ex) { foreach (Exception e in ex.InnerExceptions) { ActorEventSource.Current.Error(e); } throw; } catch (Exception ex) { ActorEventSource.Current.Error(ex); throw; } await Task.Delay(ConfigurationHelper.BackoffQueryDelay); } if (this.ObserverRegistered == null) { return; } try { Delegate[] invocationList = this.ObserverRegistered.GetInvocationList(); Task[] handlerTasks = new Task[invocationList.Length]; SubscriptionEventArgs args = new SubscriptionEventArgs(topic, enumerable, entityId); for (int i = 0; i < invocationList.Length; i++) { handlerTasks[i] = ProcessingHelper.ExecuteEventHandlerAsync((Func <SubscriptionEventArgs, Task>)invocationList[i], args); } await Task.WhenAll(handlerTasks); } catch (AggregateException ex) { foreach (Exception e in ex.InnerExceptions) { ActorEventSource.Current.Error(e); } } catch (Exception ex) { ActorEventSource.Current.Error(ex); } }
/// <summary> /// Unregisters an observer. This methods is invoked by an observer. /// </summary> /// <param name="topic">The topic.</param> /// <param name="entityId">The entity id of the observer.</param> /// <returns>The asynchronous result of the operation.</returns> public async Task UnregisterObserverAsync(string topic, EntityId entityId) { if (string.IsNullOrWhiteSpace(topic)) { throw new ArgumentException($"The {nameof(topic)} parameter cannot be null.", nameof(topic)); } if (entityId == null) { throw new ArgumentException($"The {nameof(entityId)} parameter cannot be null.", nameof(entityId)); } for (int k = 1; k <= ConfigurationHelper.MaxQueryRetryCount; k++) { try { EntityId id = await this.GetEntityIdAsync(); IReliableDictionary <string, Dictionary <Uri, ObserverInfo> > topicsDictionary = await this.StateManager.GetOrAddAsync <IReliableDictionary <string, Dictionary <Uri, ObserverInfo> > >(Constants.TopicDictionary); using (ITransaction transaction = this.StateManager.CreateTransaction()) { ConditionalValue <Dictionary <Uri, ObserverInfo> > result = await topicsDictionary.TryGetValueAsync(transaction, topic); if (!result.HasValue) { throw new ArgumentException($"{id} is not an observable for Topic=[{topic}]"); } await transaction.CommitAsync(); } using (ITransaction transaction = this.StateManager.CreateTransaction()) { ConditionalValue <Dictionary <Uri, ObserverInfo> > result = await topicsDictionary.TryGetValueAsync(transaction, topic); if (result.HasValue) { Dictionary <Uri, ObserverInfo> observers = result.Value; if (observers.ContainsKey(entityId.EntityUri)) { observers.Remove(entityId.EntityUri); ServiceEventSource.Current.Message( $"Observer successfully unregistered.\r\n[Observable]: {id}\r\n[Observer]: {entityId}\r\n[Subscription]: Topic=[{topic}]"); await topicsDictionary.AddOrUpdateAsync(transaction, topic, e => observers, (e, s) => observers); } } await transaction.CommitAsync(); } break; } catch (FabricTransientException ex) { ServiceEventSource.Current.Error(ex); if (k == ConfigurationHelper.MaxQueryRetryCount) { throw; } } catch (AggregateException ex) { foreach (Exception e in ex.InnerExceptions) { ServiceEventSource.Current.Error(e); } throw; } catch (Exception ex) { ServiceEventSource.Current.Error(ex); throw; } await Task.Delay(ConfigurationHelper.BackoffQueryDelay); } if (this.ObserverUnregistered == null) { return; } try { Delegate[] invocationList = this.ObserverUnregistered.GetInvocationList(); Task[] handlerTasks = new Task[invocationList.Length]; SubscriptionEventArgs args = new SubscriptionEventArgs(topic, entityId); for (int i = 0; i < invocationList.Length; i++) { handlerTasks[i] = ProcessingHelper.ExecuteEventHandlerAsync((Func <SubscriptionEventArgs, Task>)invocationList[i], args); } await Task.WhenAll(handlerTasks); } catch (AggregateException ex) { foreach (Exception e in ex.InnerExceptions) { ServiceEventSource.Current.Error(e); } } catch (Exception ex) { ServiceEventSource.Current.Error(ex); } }