bruceiow
bruceiow

Reputation: 83

Signalr with WinForms and Framework 4.8 MVC

I am trying to create a job notification process using signalr as the notifier. My landscape is that I have a winforms project that will be controlling the stages of the process. The system the process affects in in an MVC project.

As it stands I have these projects: Process (Winforms) System (Framework 4.8 MVC) Service (framework 4.8) SignalRSelfHost (Console)

The Service layer is referenced by both the Process and the System projects and contains the logic for each step of the process. When one stage starts or ends, it needs to call the connected clients and let them know..

I initially created the signalr stuff in the System project and was able to call the service methods from buttons on a page with start/end messages being delivered as one might expect. I then created the winforms app and called the same service methods. When I did this no messages were sent to the web client.

It is worth saying at this point that the winforms project knows nothing about signalr, it was left to the service to negotiate with signalr using this:

            var context = GlobalHost.ConnectionManager.GetHubContext<ProcessHub>();
            context.Clients.All.StageStarted(stage.started);

it then struck me that even though the two routes in to the service are running the same code, maybe the hubs they are creating are separate and will never talk as one.

So then I thought maybe I need a common signalr server separate to both the System and the Process side that both can consume. I created a note console application that will act as a self host and that then runs as:

http://localhost:8080

I can then repoint my script tags to look to that URL instead of ../signalr on the web side of things.

My problem now is how exactly can I get both the WinForms and Web apps to inject the correct context in to the service?

I tried creating a constructor for my service like this:

    private IHubContext<ProcessHub> Clients {get;set;}

    public ProcessService(IHubContext<ProcessHub> clients)
    {
        Clients = clients;
    }

But I am not using core so I couldn't figure how to create and instance of that service from my controller (I would normally use DI in .net core and register my services) I tried this but it was clearly wrong...

    public ProcessController(IHubContext<ProcessHub> clients)
    {
        _processService = new ProcessService(clients);
    }

This tells me that I need a parameterless constructor for the controller. If I create one of those, I guess I cant then use the service as intended. I haven't tried to connect win forms to the service as I assume I will come across a similar issue.

Can anyone help me with a) am I barking up completely the wrong tree... b) how can I create an instance of my service and let it know about my hub context.

Many many thanks!

Upvotes: 3

Views: 4514

Answers (1)

KHAL
KHAL

Reputation: 337

Let me show you my solution and it's working with me, I'm using asp.NET MVC .net4.6 and winforms .net4.8

  1. Notification Hub:
    [HubName("notifyHub")]
    public class NotifyHub : Hub
    {
        public void Send(string name, string message)
        {
            Clients.All.broadcastMessage(name, message);
        }

        public Task JoinGroup(string groupName)
        {
            return Groups.Add(Context.ConnectionId, groupName);

        }

        public Task LeaveGroup(string groupName)
        {
            return Groups.Remove(Context.ConnectionId, groupName);
        }
    }
  1. MVC-Controller-Action to send a msg to client:
public ActionResult CreateCustomer(Customer customer)
{
    if (ModelState.IsValid)
    {
        db.Customers.Add(customer);
        db.SaveChanges();
        var notificationHub = Microsoft.AspNet.SignalR.GlobalHost.ConnectionManager.GetHubContext<NotifyHub>();
        notificationHub.Clients.Group(YourCustomGroupOfClients.ToString()).UpdateCustomer(customer);
        return RedirectToAction("Index");
    }

    return View(customer);
}
  1. ClientSide (winforms):
//  Create this hub globally just one time
public static HubConnection hubConnection = new HubConnection(baseUrl);
readonly IHubProxy stockTickerHubProxy = Globals.hubConnection.CreateHubProxy("notifyHub");
stockTickerHubProxy.On<Customer>("UpdateCustomer", async customer =>
{
    try
    {
        var oldCust = await db.Customers.FirstOrDefaultAsync(a => a.id == customer.id);
        if (oldCust == null)
        {
            db.Customer.Add(customer);
        }
        else
        {
            oldCust.Cust_Name = customer.Cust_Name;
            oldCust.Phone1 = customer.Phone1;
        }
        await db.SaveChangesAsync();
        if (InvokeRequired) Invoke(new MethodInvoker(delegate
        {
            Refresh_DG_Customers();
        }));
    }
    catch (Exception)
    {
    }
});

Upvotes: 6

Related Questions