/// <summary> /// Calls <see cref="AzureServiceBus{TAuthenticationToken}.InstantiateReceiving()"/> /// then uses a <see cref="Task"/> to apply the <see cref="FilterKey"/> as a <see cref="RuleDescription"/> /// to the <see cref="SubscriptionClient"/> instances in <paramref name="serviceBusReceivers"/>. /// </summary> /// <param name="namespaceManager">The <see cref="NamespaceManager"/>.</param> /// <param name="serviceBusReceivers">The receivers collection to place <see cref="SubscriptionClient"/> instances into.</param> /// <param name="topicName">The topic name.</param> /// <param name="topicSubscriptionName">The topic subscription name.</param> protected override void InstantiateReceiving(NamespaceManager namespaceManager, IDictionary <int, SubscriptionClient> serviceBusReceivers, string topicName, string topicSubscriptionName) { base.InstantiateReceiving(namespaceManager, serviceBusReceivers, topicName, topicSubscriptionName); Task.Factory.StartNewSafely (() => { // Because refreshing the rule can take a while, we only want to do this when the value changes string filter; if (!ConfigurationManager.TryGetSetting(FilterKeyConfigurationKey, out filter)) { return; } if (FilterKey.ContainsKey(topicName) && FilterKey[topicName] == filter) { return; } FilterKey[topicName] = filter; // https://docs.microsoft.com/en-us/azure/application-insights/app-insights-analytics-reference#summarize-operator // http://www.summa.com/blog/business-blog/everything-you-need-to-know-about-azure-service-bus-brokered-messaging-part-2#rulesfiltersactions // https://github.com/Azure-Samples/azure-servicebus-messaging-samples/tree/master/TopicFilters SubscriptionClient client = serviceBusReceivers[0]; bool reAddRule = false; try { IEnumerable <RuleDescription> rules = namespaceManager.GetRules(client.TopicPath, client.Name).ToList(); RuleDescription ruleDescription = rules.SingleOrDefault(rule => rule.Name == "CqrsConfiguredFilter"); if (ruleDescription != null) { var sqlFilter = ruleDescription.Filter as SqlFilter; if (sqlFilter == null && !string.IsNullOrWhiteSpace(filter)) { reAddRule = true; } else if (sqlFilter != null && sqlFilter.SqlExpression != filter) { reAddRule = true; } if (sqlFilter != null && reAddRule) { client.RemoveRule("CqrsConfiguredFilter"); } } else if (!string.IsNullOrWhiteSpace(filter)) { reAddRule = true; } ruleDescription = rules.SingleOrDefault(rule => rule.Name == "$Default"); // If there is a default rule and we have a rule, it will cause issues if (!string.IsNullOrWhiteSpace(filter) && ruleDescription != null) { client.RemoveRule("$Default"); } // If we don't have a rule and there is no longer a default rule, it will cause issues else if (string.IsNullOrWhiteSpace(filter) && !rules.Any()) { ruleDescription = new RuleDescription ( "$Default", new SqlFilter("1=1") ); client.AddRule(ruleDescription); } } catch (MessagingEntityNotFoundException) { } if (!reAddRule) { return; } int loopCounter = 0; while (loopCounter < 10) { try { RuleDescription ruleDescription = new RuleDescription ( "CqrsConfiguredFilter", new SqlFilter(filter) ); client.AddRule(ruleDescription); break; } catch (MessagingEntityAlreadyExistsException exception) { loopCounter++; // Still waiting for the delete to complete Thread.Sleep(1000); if (loopCounter == 9) { Logger.LogError("Setting the filter failed as it already exists.", exception: exception); TelemetryHelper.TrackException(exception); } } catch (Exception exception) { Logger.LogError("Setting the filter failed.", exception: exception); TelemetryHelper.TrackException(exception); break; } } }); }
/// <summary> /// Calls <see cref="AzureServiceBus{TAuthenticationToken}.InstantiateReceiving()"/> /// then uses a <see cref="Task"/> to apply the <see cref="FilterKey"/> as a <see cref="RuleDescription"/> /// to the <see cref="IMessageReceiver"/> instances in <paramref name="serviceBusReceivers"/>. /// </summary> /// <param name="manager">The <see cref="Manager"/>.</param> /// <param name="serviceBusReceivers">The receivers collection to place <see cref="IMessageReceiver"/> instances into.</param> /// <param name="topicName">The topic name.</param> /// <param name="topicSubscriptionName">The topic subscription name.</param> protected override void InstantiateReceiving(Manager manager, IDictionary <int, IMessageReceiver> serviceBusReceivers, string topicName, string topicSubscriptionName) { base.InstantiateReceiving(manager, serviceBusReceivers, topicName, topicSubscriptionName); Task.Factory.StartNewSafely (() => { // Because refreshing the rule can take a while, we only want to do this when the value changes string filter; if (!ConfigurationManager.TryGetSetting(FilterKeyConfigurationKey, out filter)) { return; } if (FilterKey.ContainsKey(topicName) && FilterKey[topicName] == filter) { return; } FilterKey[topicName] = filter; // https://docs.microsoft.com/en-us/azure/application-insights/app-insights-analytics-reference#summarize-operator // http://www.summa.com/blog/business-blog/everything-you-need-to-know-about-azure-service-bus-brokered-messaging-part-2#rulesfiltersactions // https://github.com/Azure-Samples/azure-servicebus-messaging-samples/tree/master/TopicFilters #if NET452 SubscriptionClient client = serviceBusReceivers[0]; #endif #if NETSTANDARD2_0 // Since the IMessageReceiver and it's concrete implementation doesn't allow for the management of rules, we're creating a SubscriptionClient just to do the rules... it gets cleaned up somewhat below. SubscriptionClient client = new SubscriptionClient(ConnectionString, topicName, topicSubscriptionName); #endif bool reAddRule = false; try { #if NET452 IEnumerable <RuleDescription> rules = manager.GetRules(client.TopicPath, client.Name).ToList(); #endif #if NETSTANDARD2_0 Task <IList <RuleDescription> > getRulesTask = manager.GetRulesAsync(client.TopicPath, client.SubscriptionName); getRulesTask.Wait(); IEnumerable <RuleDescription> rules = getRulesTask.Result; #endif RuleDescription ruleDescription = rules.SingleOrDefault(rule => rule.Name == "CqrsConfiguredFilter"); if (ruleDescription != null) { var sqlFilter = ruleDescription.Filter as SqlFilter; if (sqlFilter == null && !string.IsNullOrWhiteSpace(filter)) { reAddRule = true; } else if (sqlFilter != null && sqlFilter.SqlExpression != filter) { reAddRule = true; } if (sqlFilter != null && reAddRule) { #if NET452 client.RemoveRule("CqrsConfiguredFilter"); #endif #if NETSTANDARD2_0 client.RemoveRuleAsync("CqrsConfiguredFilter").Wait(); #endif } } else if (!string.IsNullOrWhiteSpace(filter)) { reAddRule = true; } ruleDescription = rules.SingleOrDefault(rule => rule.Name == "$Default"); // If there is a default rule and we have a rule, it will cause issues if (!string.IsNullOrWhiteSpace(filter) && ruleDescription != null) { #if NET452 client.RemoveRule("$Default"); #endif #if NETSTANDARD2_0 client.RemoveRuleAsync("$Default").Wait(); #endif } // If we don't have a rule and there is no longer a default rule, it will cause issues else if (string.IsNullOrWhiteSpace(filter) && !rules.Any()) { ruleDescription = new RuleDescription ( "$Default", new SqlFilter("1=1") ); #if NET452 client.AddRule(ruleDescription); #endif #if NETSTANDARD2_0 client.AddRuleAsync(ruleDescription).Wait(); #endif } } catch (AggregateException ex) { if (!(ex.InnerException is MessagingEntityNotFoundException)) { throw; } } catch (MessagingEntityNotFoundException) { } if (!reAddRule) { return; } int loopCounter = 0; while (loopCounter < 10) { try { RuleDescription ruleDescription = new RuleDescription ( "CqrsConfiguredFilter", new SqlFilter(filter) ); #if NET452 client.AddRule(ruleDescription); #endif #if NETSTANDARD2_0 client.AddRuleAsync(ruleDescription).Wait(); #endif break; } catch (MessagingEntityAlreadyExistsException exception) { loopCounter++; // Still waiting for the delete to complete Thread.Sleep(1000); if (loopCounter == 9) { Logger.LogError("Setting the filter failed as it already exists.", exception: exception); TelemetryHelper.TrackException(exception); } } catch (Exception exception) { Logger.LogError("Setting the filter failed.", exception: exception); TelemetryHelper.TrackException(exception); break; } } #if NETSTANDARD2_0 client.CloseAsync(); #endif }); }