Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support strongly typed Hub Clients #131

Open
lfshr opened this issue Sep 9, 2020 · 4 comments
Open

Support strongly typed Hub Clients #131

lfshr opened this issue Sep 9, 2020 · 4 comments

Comments

@lfshr
Copy link

lfshr commented Sep 9, 2020

The Hub class allows for the use of strongly typed Hub clients. It would be great if the Azure SignalR service could also support the same! eg.

public interface IChatClient
{
    Task ReceiveMessage(string message);
}

public class ClientHub : ServerlessHub<IChatClient>
{
    ...

    [FunctionName(nameof(SendClientMessage))]
    public async Task SendClientMessage([SignalRTrigger] InvocationContext invocationContext, ILogger logger)
    {
        await Clients.All.ReceiveMessage("Hello World!");
    }
}

https://docs.microsoft.com/en-us/aspnet/core/signalr/hubs?view=aspnetcore-3.1#strongly-typed-hubs

@lfshr lfshr changed the title Support strongly typed SignalR Hubs Support strongly typed Hub Clients Sep 12, 2020
@phantomcosmonaut
Copy link
Contributor

phantomcosmonaut commented Feb 26, 2021

I believe this is easily accomplished but I can't access the assembly Microsoft.AspNetCore.SignalR.Internal.

using Microsoft.AspNetCore.SignalR;
using Microsoft.Azure.SignalR.Management;
using Microsoft.Azure.WebJobs.Extensions.SignalRService;

namespace foo
{
  public abstract class ServerlessHub<T> : ServerlessHub
      where T : class
  {
    protected ServerlessHub(IServiceHubContext hubContext = null, IServiceManager serviceManager = null)
      : base(hubContext, serviceManager)
    {
      Clients = new TypedHubClients<T>(base.Clients); // implements IHubClients so it's extendable
    }

    public new IHubClients<T> Clients { get; }
  }
}

@phantomcosmonaut
Copy link
Contributor

phantomcosmonaut commented Feb 27, 2021

So... there are some hurdles casting between IHubClients<IClientProxy> and IHubClients<T> but here's the solution I came up with and it works because I just tested it. Commencing code barf 3, 2, 1

using Microsoft.Azure.SignalR.Management;
using Microsoft.Azure.WebJobs.Extensions.SignalRService;
using System;
using System.Collections.Generic;

namespace Microsoft.AspNetCore.SignalR
{
  // This is a frankenstein way to support typed client proxies in serverless hub class
  public abstract class ServerlessHub<T> : ServerlessHub where T : class
  {
    private IHubClients<T> _clients;
    public ServerlessHub(IServiceHubContext hubContext = null, IServiceManager serviceManager = null)
      : base(hubContext, serviceManager)
    {
    }
    public new IHubClients<T> Clients
    {
      get
      {
        var typedHub = typeof(IHubCallerClients).Assembly.GetType("Microsoft.AspNetCore.SignalR.Internal.TypedHubClients`1");
        var typedHubTypeParameters = new Type[] { typeof(T) };
        var constructedTypedHub = typedHub.MakeGenericType(typedHubTypeParameters);
        var typedHubCtor = constructedTypedHub.GetConstructor(new Type[] { typeof(IHubCallerClients) });
        
        if (_clients == null)
        {
          _clients = typedHubCtor.Invoke(new object[] { new HubCallerClientsShell(base.Clients) }) as IHubClients<T>;
        }
        return _clients;
      }
      set => _clients = value;
    }
  }

  public class HubCallerClientsShell : IHubCallerClients
  {
    private IHubClients _clients;
    public HubCallerClientsShell(IHubClients clients)
    {
      _clients = clients;
    }

    public IClientProxy Caller => throw new NotImplementedException();

    public IClientProxy Others => throw new NotImplementedException();

    IClientProxy IHubClients<IClientProxy>.All => _clients.All;

    IClientProxy IHubClients<IClientProxy>.AllExcept(IReadOnlyList<string> excludedConnectionIds)
    {
      return _clients.AllExcept(excludedConnectionIds);
    }

    IClientProxy IHubClients<IClientProxy>.Client(string connectionId)
    {
      return _clients.Client(connectionId);
    }

    IClientProxy IHubClients<IClientProxy>.Clients(IReadOnlyList<string> connectionIds)
    {
      return _clients.Clients(connectionIds);
    }

    IClientProxy IHubClients<IClientProxy>.Group(string groupName)
    {
      return _clients.Group(groupName);
    }

    IClientProxy IHubClients<IClientProxy>.GroupExcept(string groupName, IReadOnlyList<string> excludedConnectionIds)
    {
      return _clients.GroupExcept(groupName, excludedConnectionIds);
    }

    IClientProxy IHubClients<IClientProxy>.Groups(IReadOnlyList<string> groupNames)
    {
      return _clients.Groups(groupNames);
    }

    IClientProxy IHubCallerClients<IClientProxy>.OthersInGroup(string groupName)
    {
      throw new NotImplementedException();
    }

    IClientProxy IHubClients<IClientProxy>.User(string userId)
    {
      return _clients.User(userId);
    }

    IClientProxy IHubClients<IClientProxy>.Users(IReadOnlyList<string> userIds)
    {
      return _clients.Users(userIds);
    }
  }
}

@davidfowl
Copy link
Member

davidfowl commented Aug 20, 2021

@vicancy Can you route this to the right place? I think this should be part of the Azure SignalR serverless SDK.

OOPS noticed I was in the right place 😄 .

@davidfowl
Copy link
Member

I would copy the code into this SDK @phantomcosmonaut

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants