Search code examples
azure-functionsazure-signalr

Cannot return multiple SignalR actions in isolated Function App


I have an Azure function app (C#, .net 7) running in ISOLATED mode with a SERVERLESS SignalR resource. I'm trying to have this function do 'multiple signalr things', like creating multiple signalr groups, as well as sending out a message (via the signalr connectionid) to the one that invoked the method.

public class DispatchMessages
{
  [SignalROutput(HubName = "Hub")]
  public IEnumerable<string> Messages { get; set; }
}

public class DoStuff
{
    [Function("RegisterLearner")]
    public async Task<DispatchMessages> RegisterLearner(
      [SignalRTrigger("Hub", "messages", "RegisterLearner", "payload")] SignalRInvocationContext invocationContext,
      RegisterParticipantPayload payload)
    {
      var actions = new List<string>
      {
        JsonSerializer.Serialize(new SignalRMessageAction("newMessage")
        {
          Arguments = new[] { "Lulz" },
          ConnectionId = invocationContext.ConnectionId
        })
      };

      return new DispatchMessages
      {
        Messages = actions.Select(x => JsonSerializer.Serialize(x))
      };
    }
}

<PackageReference Include="Microsoft.Azure.Functions.Worker" Version="1.20.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.EventGrid" Version="3.3.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Http" Version="3.1.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.SignalRService" Version="1.12.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Extensions.Timer" Version="4.1.0" />
<PackageReference Include="Microsoft.Azure.Functions.Worker.Sdk" Version="1.16.3" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="7.0.11" />
<PackageReference Include="Microsoft.Azure.AppConfiguration.Functions.Worker" Version="7.0.0" />

I started simple by just building and returning a SignalRMessageAction and it worked. I added serialization (function returns a string, the SignalRMessageAction serialized with System.Text.Json) and it worked.

But I really need to return actions of different types - hense my thought of serializing them to a string container of some sort.

The above code throws an exception somewhere, but it's not in my code because i added a try/catch with a log message, but it never fired. This is all I see in the invocation log:

Value cannot be null. (Parameter 'item')

Is there a way to return multiple SignalR actions?


Solution

  • Ok. This appears to work. The key was to create a list of 'object', and to put the output binding on the function. Apparently, this allows me to add several SignalR actions to an array, which the SignalR Live Trace Tool service sees. It's a starting point, it appears.

      public class DoStuff
      {
        [Function("RegisterLearner")]
        [SignalROutput(HubName = "Hub")]
        public async Task<IList<object>> RegisterLearner(
          [Microsoft.Azure.Functions.Worker.SignalRTrigger("Hub", "messages", "RegisterLearner", "payload")] SignalRInvocationContext invocationContext,
          RegisterParticipantPayload payload)
        {
    
          try
          {
            var actions = new List<object>
            {
              new SignalRMessageAction("newMessage")
              {
                Arguments = new[] { "Lulz" },
                ConnectionId = invocationContext.ConnectionId
              },
              new SignalRMessageAction("newMessage")
              {
                Arguments = new[] { "Lulz2" },
                ConnectionId = invocationContext.ConnectionId
              },
              new Microsoft.Azure.Functions.Worker.SignalRGroupAction(SignalRGroupActionType.Add)
              {
                GroupName = "TestGroup",
                ConnectionId = invocationContext.ConnectionId
              }
            };
    
            Logger.LogInformation(JsonSerializer.Serialize(actions));
    
            return actions;
    
          }
          catch (System.Exception ex)
          {
            Logger.LogError(ex.Message);
            Logger.LogError(ex.StackTrace);
            throw;
          }
        }