Issue with SignalR primary/secondary

Viewed 437

I have an asp dotnet core 3.1 application that uses SignalR. According to this documentation, you can add primary and secondary connections. I have this working in one of my services, but another service keeps bombing out and I can't figure out why.

Error message:

Connection string missing required properties endpoint and accesskey. (Parameter 'connectionString')

Stack:

at Microsoft.Azure.SignalR.ConnectionStringParser.Parse(String connectionString)
   at Microsoft.Azure.SignalR.ServiceEndpoint..ctor(String connectionString, EndpointType type, String name)
   at Microsoft.Azure.SignalR.ServiceEndpointManagerBase.<GetEndpoints>d__16.MoveNext()
   at System.Linq.Enumerable.DistinctIterator`1.MoveNext()
   at System.Linq.Lookup`2.Create(IEnumerable`1 source, Func`2 keySelector, IEqualityComparer`1 comparer)
   at System.Linq.GroupedEnumerable`2.GetEnumerator()
   at System.Linq.Enumerable.SelectEnumerableIterator`2.MoveNext()
   at System.Linq.Enumerable.ToDictionary[TSource,TKey,TElement](IEnumerable`1 source, Func`2 keySelector, Func`2 elementSelector, IEqualityComparer`1 comparer)
   at Microsoft.Azure.SignalR.ServiceEndpointManagerBase.GetValuableEndpoints(IEnumerable`1 endpoints)
   at Microsoft.Azure.SignalR.ServiceEndpointManagerBase..ctor(IEnumerable`1 endpoints, ILogger logger)
   at Microsoft.Azure.SignalR.ServiceEndpointManagerBase..ctor(IServiceEndpointOptions options, ILogger logger)
   at Microsoft.Azure.SignalR.ServiceEndpointManager..ctor(IOptionsMonitor`1 optionsMonitor, ILoggerFactory loggerFactory)
   at System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor, Boolean wrapExceptions)
   at System.Reflection.RuntimeConstructorInfo.Invoke(BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitRootCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitCache(ServiceCallSite callSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.VisitRootCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteVisitor`2.VisitCallSite(ServiceCallSite callSite, TArgument argument)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteRuntimeResolver.Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.DynamicServiceProviderEngine.<>c__DisplayClass1_0.<RealizeService>b__0(ServiceProviderEngineScope scope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngine.GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
   at Microsoft.Extensions.DependencyInjection.ServiceLookup.ServiceProviderEngineScope.GetService(Type serviceType)
   at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
   at Microsoft.Azure.SignalR.ServiceHubDispatcher.Start(Endpoint endpoint, Type hubType, ConnectionDelegate app)
   at Microsoft.Azure.SignalR.AzureSignalRHostedService.Start()
   at Microsoft.Azure.SignalR.AzureSignalRStartupFilter.<>c__DisplayClass0_0.<Configure>b__0(IApplicationBuilder app)
   at Microsoft.AspNetCore.Mvc.Filters.MiddlewareFilterBuilderStartupFilter.<>c__DisplayClass0_0.<Configure>g__MiddlewareFilterBuilder|0(IApplicationBuilder builder)
   at Microsoft.ApplicationInsights.AspNetCore.ApplicationInsightsStartupFilter.<>c__DisplayClass2_0.<Configure>b__0(IApplicationBuilder app)
   at Microsoft.AspNetCore.HostFilteringStartupFilter.<>c__DisplayClass0_0.<Configure>b__0(IApplicationBuilder app)
   at Microsoft.AspNetCore.Hosting.GenericWebHostService.<StartAsync>d__31.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.ConfiguredTaskAwaitable.ConfiguredTaskAwaiter.GetResult()
   at Microsoft.Extensions.Hosting.Internal.Host.<StartAsync>d__9.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.<RunAsync>d__4.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.<RunAsync>d__4.MoveNext()
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter.GetResult()
   at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.Run(IHost host)
   at SigR.Program.Main(String[] args) in G:\Git_Merc\ms-chat\WebApi\Program.cs:line 13

I have this Environment variable:

Azure:SignalR:ConnectionString:Primary

with value

Endpoint=https://<redacted>;AccessKey=<redacted>Version=1.0;

If I remove the :Primary from the key, it works, but it should work with it as well. I've also tried inserting a , such as

Azure:SignalR:ConnectionString:Test:Primary

And, like I said, I have another service working with that key. TBH, I'm not even sure why DI is picking this up on app startup?

Edit:

Adding requested code.

var sigrBuilder = services.AddSignalR().AddNewtonsoftJsonProtocol(options =>
            {
                options.PayloadSerializerSettings.Converters = new List<JsonConverter> 
                { 
                    new TransferOptionConverter() //converters for data from mercury
                };
            });
sigrBuilder.AddAzureSignalR();
2 Answers

Turns out I had the configuration duplicated in my appsettings file with a BS value and it was picking up that value instead of the one I had with the ":Primary" suffix. It would work when I did "Azure:SignalR:ConnectionString" because it was an exact match and overwrote. Once I blew that away, everything worked.

As you can see in this example you should specify the primary and secondary instaces when Adding Azure SignalR like:

var sigrBuilder = services.AddSignalR().AddNewtonsoftJsonProtocol(options =>
{
    options.PayloadSerializerSettings.Converters = new List<JsonConverter> 
    { 
        new TransferOptionConverter() //converters for data from mercury
    };
});

sigrBuilder.AddAzureSignalR(options => options.Endpoints = new ServiceEndpoint[]
{
    new ServiceEndpoint("<connection_string1>", EndpointType.Primary, "region1"),
    new ServiceEndpoint("<connection_string2>", EndpointType.Secondary, "region2"),
});
Related