Skip to content

SignalR: Give IHubContext more flexibility to deal with generics #30167

@karrocon

Description

@karrocon

Summary

Right now, it is only possible to inject generic-type IHubContext objects. Since Clients and Groups do not require the generic type we can abstract the IHubContext even further so that IHubContext<> and IHubContext<,> implement IHubContext (with no generic type).

Motivation and goals

This change gives the flexibility needed to perform generic casting from some IHubContext to IHubContext while being able to use the Clients and/or Groups properties. Can this be considered for a later major update?

In scope

You may have a unique notification service dispatching messages to some clients based on the specific hub type + connection id.

Out of scope

N/A.

Risks / unknowns

None that I can think of.

Examples

We need some codebase:

[HubName(EventStreamNames.Dashboard)]
public class MyHub : Hub { }

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class HubNameAttribute : Attribute
{
    public string HubName { get; set; }

    public HubNameAttribute(string hubName)
    {
        HubName = hubName;
    }
}

public interface IHubContextResolver
{
    IHubContext GetHubContext(string hubName);
}

internal class HubContextResolver : IHubContextResolver
{
    private readonly IServiceProvider serviceProvider;

    public HubContextResolver(IServiceProvider serviceProvider)
    {
        this.serviceProvider = serviceProvider;
    }

    public IHubContext GetHubContext(string hubName)
    {
        var hubType = AppDomain.CurrentDomain
            .GetAssemblies()
            .SelectMany(a => a.GetTypes())
            .Where(t => t.IsAssignableTo(typeof(Hub)))
            .FirstOrDefault(t => t.GetCustomAttribute<HubNameAttribute>(true)?.HubName == hubName);

        var hubContextType = typeof(IHubContext<>).MakeGenericType(hubType);

        return serviceProvider.GetService(hubContextType) as IHubContext;
    }
}

Then, it can be used by a general-purpose handler.

public class NotificationHandler : MessageHandler<SignalRNotification>
{
    private readonly IHubContextResolver _hubContextResolver;

    public NotificationHandledHandler(IHubContextResolver hubContextResolver)
    {
        _hubContextResolver = hubContextResolver;
    }

    public override async Task Handle(SignalRNotification request, CancellationToken cancellationToken)
    {
        var hub = _hubContextResolver.GetHubContext(request.Hub);
        if (hub != null)
        {
            await hub.Clients.Client(request.ConnectionId).SendCoreAsync(request.Method, new[] { request.Data });
        }
    }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    api-approvedAPI was approved in API review, it can be implementedarea-signalrIncludes: SignalR clients and serversdesign-proposalThis issue represents a design proposal for a different issue, linked in the description

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions