I want to implement SignalR with asmx webservice. The service is called from my front end application and my project is running.
My first question is, should I create SignalR hub here or I have to make another application for the Hub and use that for realtime purpose?
And second question is,
public class MyHub1: Hub
{
public bool ConnectUser(long userId)
{
UserManipulation Up = new UserManipulation();//its my class to process user//
return Up.ConnectUser(userId, Context.ConnectionId);
}
}
This is hub method to save the connection Id in database with the userid.
Now in asmx page, you know we have to create a [webmethod], so that I can call that method from front end. I want to create a webmethod which will call this hub method.
[WebMethod]
public bool ConnectUser(string UserId)
{
SignalRHub.MyHub1 myhub = new SignalRHub.MyHub1();//SignalRHub is namespace//
return myhub.ConnectUser(UserId);
}
But the context within the hub method getting null.
I don't know I'm in right way or not, but please help me how can I achieve the things to implement Hub not directly in front end, instead this type of service.
Related
I am using SignalR v2.41, which is old, but I have to use it since I am also limited to using an old version of MVC. That aside, I am also using FluentScheduler to send targeted messages to clients at intervals.
Problem is, I am keeping a dictionary of user connections in my Hub:
public class MyHub: Hub
{
public Dictionary<string, User> Connections { get; set; }
public MyHub()
{
Connections = new Dictionary<string, User>();
}
public override Task OnConnected()
{
// add connection
return base.OnConnected();
}
public override Task OnDisconnected(bool stopCalled)
{
// remove connection
return base.OnDisconnected(stopCalled);
}
}
Now in the FluentScheduler code I need to get hold of the hub for the connections list so I know which connection to send what to:
public class MyJob : IJob
{
public void Execute()
{
var hub = new DefaultHubManager(GlobalHost.DependencyResolver).ResolveHub("MyHub") as MyHub;
foreach (var conn in hub.Connections)
{
foreach (var msg in msgs)
{
hub.Clients.Client(conn.Key).send(msg);
}
}
}
}
Problem is, the hub instance I get using var hub = new DefaultHubManager(GlobalHost.DependencyResolver).ResolveHub("MyHub") as MyHub; is different from the one to which clients connect, as this one never has any connections.
How can I get the right hub instance?
The new is always a new instance so you will never get the hub where your clients are connected because you creating a new hub.
You should resolve the hub like this:
static IHubContext hubContext = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
You also can check this question.
Edit: Since you need to send messages to specific users, I would recommend to implement a class to add and remove the connections, or even better, map users to groups.
It's recommend that you always inject IHubContext than the Hub. Quote from a SignalR developer on github:
You generally shouldn't resolve the Hub out of DI. If you need to share code between your Hub and some other component, I'd suggest using either IHubContext or putting the shared code in a separate DI service instead.
Also you should not add the Hub as a singleton:
SignalR expects the Hub to be created separately for each message. You need to add it as a Transient service if you want your Hub to be in DI.
and
Because instances of the Hub class are transient, you can't use them to maintain state from one method call to the next. Each time the server receives a method call from a client, a new instance of your Hub class processes the message. To maintain state through multiple connections and method calls, use some other method such as a database, or a static variable on the Hub class, or a different class that does not derive from Hub.
More documentation about Hub object lifetime.
I am using Signalr 1.1.4 because im still using .net4 so cant upgrade to signalr 2.
Basically i want to post a message from the server to just the caller to avoid messages being sent to any client that did not start the process off.
My hub class looks like this
public class UpdateHub : Hub
{
/// <summary>
/// Sends the message.
/// </summary>
/// <param name="progressMessage">The progress message.</param>
public void SendMessage(string progressMessage)
{
Clients.Client(Context.ConnectionId).sendMessage(string.Format(progressMessage));
}
}
my javascript looks like this
// get handle to subscriptionUpload hub generated by SignalR
var updateHub = $.connection.UpdateHub;
// establish the connection to the server and start server-side operation
$.connection.hub.start();
updateHub.client.sendMessage = function (message)
{
$("container").empty();
$("container").append(message);
}
Now in my controller action method i would like to do something like this
UpdateHub hub = new UpdateHub();
hub.SendMessage("process has started");
//continue on with long process
hub.SendMessage("process has ended");
Is this even possible?
What we can find in documentation documentation:
You don't instantiate the Hub class or call its methods from your own
code on the server; all that is done for you by the SignalR Hubs
pipeline. SignalR creates a new instance of your Hub class each time
it needs to handle a Hub operation such as when a client connects,
disconnects, or makes a method call to the server.
Because instances of the Hub class are transient, you can't use them
to maintain state from one method call to the next. Each time the
server receives a method call from a client, a new instance of your
Hub class processes the message. To maintain state through multiple
connections and method calls, use some other method such as a
database, or a static variable on the Hub class, or a different class
that does not derive from Hub. If you persist data in memory, using a
method such as a static variable on the Hub class, the data will be
lost when the app domain recycles.
And then:
If you want to send messages to clients from your own code that runs
outside the Hub class, you can't do it by instantiating a Hub class
instance, but you can do it by getting a reference to the SignalR
context object for your Hub class.
You can get the context of your hub: GlobalHost.ConnectionManager.GetHubContext<YourHub>()
and then you can use it to call methods on the client side like this:
context.Clients.All.YourMethod(params);
or
context.Clients.Client(someConnectionID).YourMethod(params);
But in this case you won't be able to use Context.ConnectionId in this methods, because you don't have a direct connection to your hub. In this case you will need to store your connections somewhere (static variables, cache, db etc) and then use it to determine which client should be called.
Hope it will help.
I'm new to .NET and WCF framework. Currently, I'm building a simple windows application to consume WCF service. The windows client need to pass username parameter to WCF Service to save to database. How can I do that without passing parameter in every service requests ?
One thing to note is this service will call a method in another project to save to database , so actually I want to access the username parameter at this project layer.
Form1.cs
public async void Save()
{
using(var client = new MyChannelFactory<WCFService>("WCFService"))
{
await Task.Run(() => client.Proxy.SaveData());
}
}
WCFService.cs
public void SaveData()
{
var _dataBL = new DataBL();
_dataBL.SaveData();
}
DataBL.cs
public void SaveData();
{
//need to get username from client
string user = GetUserName();
//save to database
}
Something to note:
WCFService.cs and DataBL.cs are in different projects.
I'm not adding Service Reference to client project, instead I'm using a ChannelFactory to create service proxy.
I'm using wsHttpBinding
I'm not using Windows Authentication
This should be a common request but after read many articles from internet, I still couldn't make it work.
Thanks for your help.
I have inside my hub a method that is used to send to specific clients that a ticket was updated. However I want it to send it to the server inside the controller and to specific users, you can see how I am doing it below:
I am using the following method to send to the specific clients:
public void NotifyUpdatedTicket(Ticket ticket, string note, params string[] aliases)
{
foreach (string a in aliases)
{
foreach (string connectionId in _allConnections.GetConnections(a))
{
Clients.Client(connectionId).notifyUpdatedTicket(ticket, note);
}
}
}
The _allConnections is the same as the class in http://www.asp.net/signalr/overview/guide-to-the-api/mapping-users-to-connections#inmemory
However instead of passing 1 user I passed a list of aliases and then get their connections and send to their connection ids.
However it does not seem to be sending properly it sends it to all clients. Here is the command I am using to call the method in my controller:
GlobalHost.ConnectionManager.GetHubContext<TicketHub>().Clients.All.notifyUpdatedTicket(viewModel.Current, viewModel.NewNote, viewModel.Current.OpenedBy, viewModel.Current.AssignedTo);
When I send the above command with the specific OpenedBy and AssignedTo, it sends it all users when it shouldn't.
Am I missing something from calling it in the controller that sends it only to specific users or something?
Even when I put in the persons alias directly into the hub method it still sends to everyone. I assume this has to do with the controller method.
This
Clients.All.notifyUpdatedTicket
will send a message to all clients. It won't call your method with this signature (I assume that's what you're trying to do):
public void NotifyUpdatedTicket(Ticket ticket, string note, params string[] aliases)
I assume this is a method you added to your hub class.
It's not a good idea to put methods in a hub class if their sole purpose is to be called from the server-side.
Anyway, if you do, you need to use GetHubContext<TicketHub>() from within that method even if it is part of the hub class, because the hub context is set up as part of the hub pipeline when a request to that hub arrives. Without that, the hub is just a regular class and you don't have access to dynamic members like Clients.
So, all in all, it seems like you're confused about some fundamentals. I'd suggest moving NotifyUpdatedTicket to a separate class that also caches the relevant hub context, then call that method when needed (e.g. from a MVC controller method if that is your intent).
According to the documentation I have read, in order to send a message to a client, I just need to call:
var hubContext = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
hubContext.Clients.All.foo(msg);
So... does the Hub class need any methods? If not, then all I have is an empty class:
public class MyHub : Hub
{
}
which just seems like a pointless setup. Am I implementing this incorrectly? because it makes more sense to have methods in a hub class, and then call those methods to send a message to the client.
Also, in the hub itself, I can access Context.connectionId, so that I can get the requestor's connection Id and then stop the message from being fired to that client.... If a Hub shouldn't have methods, then is there a way to access the requestor's connection id?
Yes, you need an empty HUB class declaration, because - It is actually just a proxy between the JS client and the controller so it could be empty since all methods are called via the Clients dynamic variable.
I mean without this , you can work, but you have to write JS for that. This is explained briefly in following link.
For more info refer this link - http://www.asp.net/signalr/overview/signalr-20/hubs-api/hubs-api-guide-javascript-client#genproxy