1

I'm developing a .NET MAUI frontend and an ASP.NET Core Web API backend with SignalR for real-time order notifications. Everything works perfectly when running locally, but after deploying the API to Azure App Service, the SignalR connection from the MAUI app fails.

I've already configured web sockets and ARR affinity to "On" in the Azure App Service settings and set up CORS in my Program.cs to allow AllowAnyHeader(), AllowAnyMethod() and AllowCredentials().

Here is the frontend code:

hubConnection = new HubConnectionBuilder()
  .WithUrl($"{TokenEndpoints.RegisterAlertEndPoint}?userId={userId}&companyKy={CompanyKey}", options => {
      options.SkipNegotiation = true;
      options.Transports = HttpTransportType.WebSockets;
  })
  .ConfigureLogging(logging =>
  {
      logging.AddDebug();
      logging.SetMinimumLevel(LogLevel.Debug); // Log everything
  })
  .Build();

 // Start the SignalR connection
 // await hubConnection.StartAsync();

 Task.Run(async () =>
 {
     await InvokeAsync(async () =>
     {
         await hubConnection.StartAsync();
     });
 });

 // Handle incoming notifications
 hubConnection.On<string>("ReceiveMessage", (message) =>
 {
     // Add notification to the list and refresh UI
     //notifications.Add(message);
     NotificationMessage = message;
     Console.WriteLine($"Notification received: {message}");
     MainThread.BeginInvokeOnMainThread(() =>
     {
         // Show the notification dialog
         ShowNotificationDialog();
        
     });

 });

This is the backend Web API code:

 public class OrderHub : Hub
 {
     private static Dictionary<string, Dictionary<string, List<string>>> companyUserConnections = new Dictionary<string, Dictionary<string, List<string>>>();
     private readonly ConnectionMappingService _connectionMappingService;
     private readonly ILogger<OrderHub> _logger;

     public OrderHub(ConnectionMappingService connectionMappingService, ILogger<OrderHub> logger)
     {
         _connectionMappingService = connectionMappingService;
         _logger = logger;
     }

     // When a client connects to the hub
     public override async Task OnConnectedAsync()
     {
         // Get the userId and companyKey from the query string
         var userId = Context.GetHttpContext().Request.Query["userId"].ToString();
         var companyKey = Context.GetHttpContext().Request.Query["companyKy"].ToString();
         var userName = Context.GetHttpContext().Request.Query["userName"].ToString();

         if (!string.IsNullOrEmpty(userId) && !string.IsNullOrEmpty(companyKey))
         {
             // Add the connection to the dictionary
             if (!companyUserConnections.ContainsKey(companyKey))
             {
                 companyUserConnections[companyKey] = new Dictionary<string, List<string>>();
             }

             if (!companyUserConnections[companyKey].ContainsKey(userId))
             {
                 companyUserConnections[companyKey][userId] = new List<string>();
             }

             companyUserConnections[companyKey][userId].Add(Context.ConnectionId);
             
             _connectionMappingService.Add(companyKey, userId, Context.ConnectionId);
             _logger.LogInformation($"New connection: {Context.ConnectionId}");

             await Clients.Caller.SendAsync("ReceiveMessage", $"Welcome, user {userName} !");
         }

         await base.OnConnectedAsync();
     }

     public override async Task OnDisconnectedAsync(Exception? exception)
     {
         // Get the userId and companyKey from the query string
         var userId = Context.GetHttpContext().Request.Query["userId"].ToString();
         var companyKey = Context.GetHttpContext().Request.Query["companyKy"].ToString();

         if (!string.IsNullOrEmpty(userId) && !string.IsNullOrEmpty(companyKey) && companyUserConnections.ContainsKey(companyKey))
         {
             if (companyUserConnections[companyKey].ContainsKey(userId))
             {
                 companyUserConnections[companyKey][userId].Remove(Context.ConnectionId);

                 if (!companyUserConnections[companyKey][userId].Any())
                 {
                     companyUserConnections[companyKey].Remove(userId);
                 }

                 if (!companyUserConnections[companyKey].Any())
                 {
                     companyUserConnections.Remove(companyKey);
                 }

                 _connectionMappingService.Remove(companyKey, userId);
             }
         }

         _logger.LogInformation($"connection Disconnected: {Context.ConnectionId}");
         await base.OnDisconnectedAsync(exception);
     }
 }

I tested

await _hubContext.Clients.All.SendAsync("ReceiveMessage", "New Order Testing");

this to send messages to the frontend. When I try it locally, it works. But after publishing it to Azure, it's not working.

Is the Azure Free Tier (for App Service) likely the root cause of this connection failure?

Please help me solve this issue.

7
  • 1
    Obligatory: No, the bug is in your code (and mine). A lot of developers use SignalR on free Azure tiers, including all Blazor developers. We can't guess what the application is doing right now, not even if it uses the Azure SignalR service or a SignalR hub in the application itself. If you don't use Azure SignalR, check the Configure the app in Azure App Service Commented Jul 17 at 7:19
  • 1
    Post your code and the actual full exceptions. "Fails" doesn't explain anything. A timeout is very different from a 404 or an SSL exception. Are you using the correct URL for the hub? Try creating a unit test that connects to the deployed app. It's a lot easier to troubleshoot problems when you have a unit test you can quickly change or debug. You can also use a debugging proxy like Fiddler to see what's actually going on in the connection, what the client sends and what the server responds. Commented Jul 17 at 7:24
  • my code worked in local host but not working in azure. that is the issue i don't understand Commented Jul 17 at 8:04
  • 1
    You already wrote that. It explains nothing. "Not working" isn't an error message. For all we know you're using the wrong URL. I only guess you're not using Azure SignalR because you mention configuring WebSockets and ARR. Create a unit test that does nothing more than use Microsoft.AspNetCore.SignalR.Client's HubConnectionBuilder to connect and log events. Does await connection.StartAsync() succeed or throw, and what's the full exception? Handle and log the Closed and reconnection events. What happens? Commented Jul 17 at 8:16
  • The section App Service Plan limits in the Publish to Azure App Service doc pages mentions limit per tier. If you follow the link to the Azure App Service limits table you'll see Free Tier allows 5 concurrent websocket connections. A unit test won't hit that limit, but running multiple MAUI apps at the same time will. Commented Jul 17 at 8:20

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.