Open Closed

Issue Implementing Azure's Managed SignalR service with an ABP Blazor Server application #4993


User avatar
0
balessi75 created

ABP Commercial 7.0.1 / Blazor Server / EF / Non tiered / Separate Host DB, Separate Tenant DBs / Lepton Theme

Hi,

We are attempting to use Azure's managed SignalR service in our Blazor Server application in order to make it more scalable.

Using MS documentation, we created a SignalR resource in Azure and used the issued SignalR connection string when configuring services...

context.Services.AddSignalR().AddAzureSignalR(<SignalR-Connection-string-here>);

The problem we are having is that the application no long works as expected once this line of code is introduced. What we are seeing is the logic for our tenant resolver (based on subdomain) no longer works. When logging in under tenant1.myapp.com, the UI seems to be logging into the host instead of tenant1. No exception is logged.

As soon as we remove the the call to AddSignalR().AddAzureSignalR everything works as expected.

Is there something specific with a templated Blazor Server solution in ABP that needs to be accounted for?

Any thoughts/suggestions? Thanks in advance,

Brian


12 Answer(s)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Can you share an online URL so I can test it?

  • User Avatar
    0
    balessi75 created

    hi

    Can you share an online URL so I can test it?

    I sent you a private email with a URL and credentials.,

    Thanks

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    I can sign in.

    But I continue to get errors.

    An error has occurred. This application may no longer respond until reloaded. Reload

    Can you share the logs?

    https://learn.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.components.server.circuitoptions.detailederrors?view=aspnetcore-7.0

    services.AddServerSideBlazor().AddCircuitOptions(e=> {
            e.DetailedErrors = true;
        });
    
  • User Avatar
    0
    balessi75 created

    Hi,

    That's strange, I don't get the error An error has occurred. This application may no longer respond until reloaded. Reload

    I just get an empty menu on the left with no tenant designator in the upper right next to the user name.

    Which URL were you getting that with, the demo.dev or demo.qa URL?

    I added to the problem url (demo.qa) in program.cs

    services.AddServerSideBlazor().AddCircuitOptions(e=> {
            e.DetailedErrors = true;
        });
    

    and redeployed to the problem URL demo.qa (URL with the SignalR service turned on) and I don't see any errors in the logs or in the browser tools. I just emailed you the full log after the redeploy.

    Here is a comparison of the browser console ....

    demo.dev url with no SignalR service enabled - works perfectly fine:

    demo.qa url with SignalR service enabled - does not work:

  • User Avatar
    0
    balessi75 created

    I found this from 4 months ago https://support.abp.io/QA/Questions/4376/Permissions-not-working-with-Azure-SignalR-Service

    Was there ever a resolution to this issue?

    This appears to be the same kind of issue. We see the azure signalr connection taking place, but the abp menu/permission is all pointing to the host , even though we are logging into a particular tenant.

    Also, the app doesn't need to be deployed to Azure to see the issue. The issue happens in local development when we have the signalR connection string defined in secrets.json.

  • User Avatar
    0
    balessi75 created

    Hi @maliming

    I was able to recreate the problem with a brand new templated ABP project at version 7.0.1 (Blazor Server).

    Steps to reproduce;

    1. Add the following to appsettings.json
    "Azure": {
        "SignalR": {
          "ConnectionString": "connection string I emailed you yesterday"
        }
      }
    
    1. Add the latest Microsoft.Azure.SignalR package reference to the Blazor project
    2. Add the following to the end of ConfigureServices
    public override void ConfigureServices(ServiceConfigurationContext context)
    {
        ...
        context.Services.AddSignalR().AddAzureSignalR();
    }
    
    1. Login as the host and create a role called "Host Role"
    2. When logged in as the host, create a tenant
    3. Login as the tenant and go to the role page and you'll see that your logging into the host's data (seeing the "Host Role")
    4. Comment out context.Services.AddSignalR().AddAzureSignalR(); and then login as the tenant and you'll now be properly logged into the tenant

    Logged in as Host

    Logging in as Tenant

    Logged in as Tenant - seeing host data and no tenant indicator on upper right of screen

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Thanks, I will check it.

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    hi

    Please test below code.

            context.Services.AddSignalR(options =>
            {
                options.AddFilter<AbpMultiTenantHubFilter>();
            }).AddAzureSignalR();
    
    
    using System;
    using System.Security.Principal;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.SignalR;
    using Microsoft.Extensions.DependencyInjection;
    using Volo.Abp.MultiTenancy;
    using Volo.Abp.Security.Claims;
    
    namespace FM.Test.Blazor;
    
    public class AbpMultiTenantHubFilter : IHubFilter
    {
        public virtual async ValueTask<object> InvokeMethodAsync(HubInvocationContext invocationContext, Func<HubInvocationContext, ValueTask<object>> next)
        {
            var currentPrincipalAccessor = invocationContext.ServiceProvider.GetRequiredService<ICurrentPrincipalAccessor>();
            using (currentPrincipalAccessor.Change(invocationContext.Context.User))
            {
                using (invocationContext.ServiceProvider.GetRequiredService<ICurrentTenant>().Change(currentPrincipalAccessor.Principal.FindTenantId()))
                {
                    return await next(invocationContext);
                }
            }
        }
    
        public virtual Task OnConnectedAsync(HubLifetimeContext context, Func<HubLifetimeContext, Task> next)
        {
            return Task.CompletedTask;
        }
    
        public virtual Task OnDisconnectedAsync(HubLifetimeContext context, Exception exception, Func<HubLifetimeContext, Exception, Task> next)
        {
            return Task.CompletedTask;
        }
    }
    
    
  • User Avatar
    0
    balessi75 created

    Thanks @maliming

    That looks good as far as the correct tenant data is shown in each page, but I noticed that the tenant name next to the user name on the upper right of the screen still doesn't show (Lepton theme - tenant\user).

    Any thoughts on that?

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer
    
    using System;
    using System.Security.Principal;
    using System.Threading.Tasks;
    using Microsoft.AspNetCore.SignalR;
    using Microsoft.Extensions.DependencyInjection;
    using Volo.Abp.MultiTenancy;
    using Volo.Abp.Security.Claims;
    
    namespace FM.Test.Blazor;
    
    public class AbpMultiTenantHubFilter : IHubFilter
    {
        public virtual async ValueTask<object> InvokeMethodAsync(HubInvocationContext invocationContext, Func<HubInvocationContext, ValueTask<object>> next)
        {
            var currentPrincipalAccessor = invocationContext.ServiceProvider.GetRequiredService<ICurrentPrincipalAccessor>();
            using (currentPrincipalAccessor.Change(invocationContext.Context.User))
            {
                var tenantId = currentPrincipalAccessor.Principal.FindTenantId();
                string tenantName = null;
                if (tenantId != null)
                {
                    tenantName = (await invocationContext.ServiceProvider.GetRequiredService<ITenantStore>().FindAsync(tenantId.Value))?.Name;
                }
                using (invocationContext.ServiceProvider.GetRequiredService<ICurrentTenant>().Change(currentPrincipalAccessor.Principal.FindTenantId(), tenantName))
                {
                    return await next(invocationContext);
                }
            }
        }
    
        public virtual Task OnConnectedAsync(HubLifetimeContext context, Func<HubLifetimeContext, Task> next)
        {
            return Task.CompletedTask;
        }
    
        public virtual Task OnDisconnectedAsync(HubLifetimeContext context, Exception exception, Func<HubLifetimeContext, Exception, Task> next)
        {
            return Task.CompletedTask;
        }
    }
    
    
  • User Avatar
    0
    balessi75 created

    Hi @maliming

    Thank you. This looks good and is tremendously helpful. I will continue to test...

  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    : )

Made with ❤️ on ABP v8.2.0-preview Updated on March 25, 2024, 15:11