17

I am using SignalR 2 and I can not figure out how I can use my Hub methods e.g from inside a controller action.

I know I can do the following:

var hub = GlobalHost.ConnectionManager.GetHubContext<T>();
hub.Clients.All.clientSideMethod(param);

But that executes the method directly on the client side.

What if I have business logic inside my server side ClientSideMethod(param) method I want to call from my controller the same way as when it is called from the client side?

At the moment I use public static void ClientSideMethod(param) inside my hub and in that method I use the IHubContext from the ConnectionManager.

Is there no better was of doing this?

The following is not working (anymore in SignalR 2?):

var hubManager = new DefaultHubManager(GlobalHost.DependencyResolver);
instance = hubManager.ResolveHub(typeof(T).Name) as T;
instance.ClientSideMethod(param);

There I get a "Hub not created via Hub pipeline not supported" exception, when accessing the Clients.

Christoph Fink
  • 22,727
  • 9
  • 68
  • 113
  • Why not do it from the client, which i assume is JS, you can do an Ajax request and do what you need on the controller – Mahmoud Darwish Jul 28 '13 at 13:22
  • 1
    @MEYWD: Because for example I have an admin interface which I would like to update when "something happens". E.g. the client visits a specific page, so I would like to trigger the hub method in the controller action from that page. The actual client does not care about that, so why should he post that message? – Christoph Fink Jul 28 '13 at 16:13
  • i have done something similar, i have clients and admins(special client), the admin send a command and waits for the response from the client, to do that i made a list of admins and a list of clients on the hub, the admin chooses a client, reserve it so no other admin use it, then sends a command, the hub relay it to the client, the client returns the response to the hub, which in return is relayed to the admin and the response is printed on the screen – Mahmoud Darwish Jul 28 '13 at 16:27

2 Answers2

13

It might work to create a "helper" class that implements your business rules and is called by both your Hub and your Controller:

public class MyHub : Hub
{
    public void DoSomething()
    {
        var helper = new HubHelper(this);
        helper.DoStuff("hub stuff");
    }
}

public class MyController : Controller
{
    public ActionResult Something()
    {
        var hub = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
        var helper = new HubHelper(hub);
        helper.DoStuff("controller stuff");
    }
}

public class HubHelper
{
    private IHubConnectionContext hub;

    public HubHelper(IHubConnectionContext hub)
    {
        this.hub = hub;
    }

    public DoStuff(string param)
    {
        //business rules ...

        hub.Clients.All.clientSideMethod(param);
    }
}
michaelrp
  • 663
  • 4
  • 9
  • +1 as it is already a better approach then mine, but I still would like a possiblity to use the Hub methods "directly" as it seems "clearer" and "easier to understand" to me... – Christoph Fink Jul 27 '13 at 12:15
  • @ChrFin did you ever find a way to successfully call the hub methods from within the assembly? Without being able to call an instance of the hub, this option appears to be the most DRY and compartmentalized. – Brandon Wittwer Aug 13 '14 at 13:20
  • @BrandonWittwer: No, not really. I just did not mark the answer as accepted as I am still hoping for a "native solution". I will add an answer with my solution I have at the moment... – Christoph Fink Aug 13 '14 at 13:25
11

As I did not find a "good solution" I am using @michael.rp's solution with some improvements:

I did create the following base class:

public abstract class Hub<T> : Hub where T : Hub
{
    private static IHubContext hubContext;
    /// <summary>Gets the hub context.</summary>
    /// <value>The hub context.</value>
    public static IHubContext HubContext
    {
        get
        {
            if (hubContext == null)
                hubContext = GlobalHost.ConnectionManager.GetHubContext<T>();
            return hubContext;
        }
    }
}

And then in the actual Hub (e.g. public class AdminHub : Hub<AdminHub>) I have (static) methods like the following:

/// <summary>Tells the clients that some item has changed.</summary>
public async Task ItemHasChangedFromClient()
{
    await ItemHasChangedAsync().ConfigureAwait(false);
}
/// <summary>Tells the clients that some item has changed.</summary>
public static async Task ItemHasChangedAsync()
{
    // my custom logic
    await HubContext.Clients.All.itemHasChanged();
}
Christoph Fink
  • 22,727
  • 9
  • 68
  • 113