I want to implement a facebook like notification system in ASP.NET MVC 3 : notifications are sent to a specific user to notify him for an action on one of his items.
Is signalr suited for such requirement?
How could i send a notification to a specific user (all opened sessions of this user) using SignalR?
Edit
Ok, Here what i did
In the client side
$(function () {
// Proxy created on the fly
var chat = $.connection.chat;
var username = '#Html.ViewContext.HttpContext.User.Identity.Name';
// Declare a function on the chat hub so the server can invoke it
chat.addMessage = function (message) {
$('#messages').append('<li>' + message + '</li>');
};
// Start the connection
$.connection.hub.start(function (){
chat.join(username);
});
});
In the server side
public class Chat : Hub
{
public void Join(string username)
{
AddToGroup(username);
}
}
And every time i need to notify a user in the controller i do the following:
IConnectionManager connectionManager = AspNetHost.DependencyResolver.Resolve<IConnectionManager>();
dynamic clients = connectionManager.GetClients<Chat>();
clients[username].addMessage("test");
Yes, SignalR is a good choice for that. Take a look at the documentation regarding Hubs (server and JS client).
You need to implement the server logic to associate your client's session with SignalR's session. You can use groups to notify all the open sessions of each user.
It is appropriate for this or you use polling, those are the two choices.
Heres a brand new video from today on this:
http://channel9.msdn.com/Shows/Web+Camps+TV/Damian-Edwards-and-David-Fowler-Demonstrate-SignalR?utm_source=dlvr.it&utm_medium=twitter
Related
Hi I'm trying to send a message to a group using the Azure Signal R Serverless JS Client Js Library.
I can do this from the Azure Serverless Function as simply as:
await signalRMessages.AddAsync(
new SignalRMessage
{
GroupName = m.GroupName,
Target = m.Target,
Arguments = new[] { m.Message }
});
*where signalRMessages = IAsyncCollector signalRMessages
How can I send this same message from the js library?
trying to send a message to a group using the Azure Signal R Serverless
You can refer to this github repo that shows with sample code how to implement group broadcasting functionality in Azure functions with Azure SignalR Service.
Add user to a group using the SignalRGroupAction class
return signalRGroupActions.AddAsync(
new SignalRGroupAction
{
ConnectionId = decodedfConnectionId,
UserId = message.Recipient,
GroupName = message.Groupname,
Action = GroupAction.Add
});
On client side, make request to endpoint to add a user to a group
function addGroup(sender, recipient, connectionId, groupName) {
return axios.post(`${apiBaseUrl}/api/addToGroup`, {
connectionId: connectionId,
recipient: recipient,
groupname: groupName
}, getAxiosConfig()).then(resp => {
if (resp.status == 200) {
confirm("Add Successfully")
}
});
}
Test Result
Updated:
Q: "send the message from the JS Client straight from the socket".
A: From here, we can find:
Although the SignalR SDK allows client applications to invoke backend logic in a SignalR hub, this functionality is not yet supported when you use SignalR Service with Azure Functions. Use HTTP requests to invoke Azure Functions.
It's seems like this is now possible ...
https://learn.microsoft.com/en-us/azure/azure-signalr/signalr-concept-serverless-development-config#sending-messages-from-a-client-to-the-service
Sending messages from a client to the service If you have upstream
configured for your SignalR resource, you can send messages from
client to your Azure Functions using any SignalR client. Here is an
example in JavaScript:
JavaScript
connection.send('method1', 'arg1', 'arg2');
I would like to notify online users that a new chat room has been created. I don't need to notify offline users once they come online.
I looked at AbpNotifications but this seems to persist notifications and notify a user once they come online. I only want to notify currently online users.
I looked at SignalR integration but could not find a way for the server to initiate a message. For instance, I might want the ApplicationService.ChatRoom.Create method to initiate the message.
I did find documentation for how to get SignalR (outside of ABP) to initiate a message: https://learn.microsoft.com/en-us/aspnet/signalr/overview/getting-started/tutorial-server-broadcast-with-signalr#server
Inject and use IRealTimeNotifier.
private async Task SendRealTimeNotificationsAsync(string message, UserIdentifier[] users)
{
var data = new MessageNotificationData(message);
var notification = new TenantNotification { Data = data };
var userNotifications = users.Select(user => new UserNotification
{
TenantId = user.TenantId,
UserId = user.UserId,
Notification = notification
});
await RealTimeNotifier.SendNotificationsAsync(userNotifications.ToArray());
}
More info:
https://aspnetboilerplate.com/Pages/Documents/Notification-System#real-time-notifications
So I am fairly new with signalR and had worked with it a bit with MVCs. Now I am using it in webapi with angularjs and am a bit confused or have forgotten of what I have done. I am using bearer tokens with webapi and am trying to create a notification system.
What I want to figure out is the proper way of using angularjs with signalR. I see many people use the proxy on/invoke. Is the proxy.on is when I call the hubcontext from the server as so:
IHubContext hubContext = GlobalHost.ConnectionManager.GetHubContext<NotificationHub>();
hubContext.Clients.User(UserId).broadcastNotification("Good morning! The time is " + DateTime.Now.ToString());
and the proxy.invoke method is from the client side? If so, which would be the best way for using notification systems (I would assume the proxy.on)?
My second question is more on sending notifications to specific users. For sending requests to specific users, I would assume you would want to do this on the hub as so:
public void SendNotification(string userId)
{
Clients.User(userId).broadcastNotification("Good morning! The time is " + DateTime.Now.ToString());
}
My startup is something like this:
public class MyProvider : IUserIdProvider
{
public string GetUserId(IRequest request)
{
var userId = request.User.Identity.Name;
return userId.ToString();
}
}
public void Configuration(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
ConfigureOAuth(app);
WebApiConfig.Register(config);
app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
app.UseWebApi(config);
Database.SetInitializer(new MigrateDatabaseToLatestVersion<AuthContext, Travelfy.API.Migrations.Configuration>());
GlobalHost.DependencyResolver.Register(typeof(IUserIdProvider), () => new MyProvider());
app.MapSignalR("/hubs", new HubConfiguration());
}
When I refresh my pages, I notice that all my userids are all empty strings "". I was reading that maybe it was due to using bearer tokens. If so, how would I use bearer tokens to specific the userId that I would want to send to? When I use the Clients.All everything works fine, so I'm assuming it has to be something with the startup/userIds I am getting.
Thanks
To answer your first question:
Which would be the best way for using notification systems
If you want to push notifications from the server towards the client, you have to define a new handler is to define a method on the client (with the generated proxy) like this:
How to define methods on the client that the server can call
If you want the client to call a method that lies on the server, you have to use this method:
How to call server methods from the client
To answer your second question:
For sending requests to specific users, I would assume you would want
to do this on the hub
You could use the connection ID of the client you wish to target. See this:
Calling client methods
So after a while, I was able to figure out the right answer. Because I was using bearerTokens, I really had to determine another method of obtaining the userId rather than just relying on request.User.Identity.Name. What I needed to do was pass my bearerToken to the connection.qs value.
connection.qs = { Bearer: token };
Once I was able to do that I had to route the find my user based on the token that I had sent in.
var token = request.QueryString.Get("Bearer");
var authenticationTicket = Startup.OAuthBearerOptions.AccessTokenFormat.Unprotect(token);
I'm working an ASP.net MVC cloud service project running two roles, a web role and a worker role. One of the pages in the web role initiate a request to build an APK file, building an APK file on the server can take anywhere from 1-5 minutes. So we came up with the following flow:
The user initiate the APK building process on the page.
The request is routed to our mvc action, creating a new message on an Azure Storage Queue.
The Worker role is always polling from the queue and starts the APK building process. Now that the APK is ready we want ideally to notify the user by:
(a) sending an email, which is working now. and (b) notifying the user on the page using SignalR.
Our problem is now in the SignalR part, how can we notify the user on the page that the APK is ready and he can download it.
EDIT - Copying contents of the first comment for the sake of completeness -
I've looked the question again and I understand that you are using a worker role to poll the queue. In this case, you can make your work role a .Net SignalR client that connects to the APK signalR hub on the web role. The signlaR hub on the web role can simple forward any message it receives from the .Net client to the javascript client (browser).
I would recommend going through the below links
Hubs API Guide - Server
Hubs API Guide - Javascript Client
before going through rest of the answer.
As can be understood from the above two links, SignalR enables the server to 'push' data to the client. In order for this to happen, you require two things -
A signalR hub - this is the 'hub' to which clients can subscribe to in order to receive messages.
A client connected to the hub
Your signalR hub on the server can look something like this -
public class APKHub : Hub
{
public async Task JoinGroup(string groupName)
{
await Groups.Add(Context.ConnectionId, groupName);
Clients.Group(groupName).sendMessage(Context.User.Identity.Name + " joined.");
}
public Task LeaveGroup(string groupName)
{
return Groups.Remove(Context.ConnectionId, groupName);
}
public void NotifyUser(string userId)
{
this.Clients.Group(userId).notify();
}
}
On the client, your code might look something like this -
var notificationHandler = function () {
var url;
var user;
var init = function (notificationUrl, userId) {
url = notificationUrl;
user = userId;
connectToAPKHub();
}
var connectToAPKHub = function () {
$.connection.hub.url = url;
var apk= $.connection.apkHub;
apk.client.notifyUser = function (user) {
console.log(user);
}
apk.client.addMessage = function (message) {
console.log(message);
}
$.connection.hub.start().done(function () {
console.log('connected to apkhub');
apk.server.joinGroup(user);
})
}
return {
init: init
}
}();
The notificationUrl is the URL that the signalR server is listening to.
This sets up your basic hub on the server and you should now be able to connect your client to the signalR hub. When the APK is built, you can use the following code (place it anywhere - for ex - in a controller action) to actually push a message to the concerned client -
var apkHub = GlobalHost.ConnectionManager.GetHubContext<APKHub>();
apkHub.Clients.Group(groupName).notifyUser(groupName);
The groupName can be an identifier that uniquely identifies a user.
Hope this helps.
I'm hoping to use SignalR to provide updates to the client, the updates are going to come from a message table which is updated when things happen across the application..
My problem is that the application will have around 500-600 concurrent users and I cant have all them having a connection to the database and constantly polling against the table..
What id like to do is have a single thing{?} polling the table and then updating the hubs rather than each connection polling.. I was thinking of using a singleton for this? so maybe when the application starts something is created that will then do all the work really..
My question is - say I had a singleton that had an event which was fired every time there was an update.. what would the performance be like for say 500 controllers subscribing to this event?
Also.. if there is a better way to do this then pleases say.. this is my first and only idea sadly!
any help would be fantastic!
EDIT: the data is bring provided by a legacy application and I have no control over how the data is entered so database polling will be needed.
ste.
I'd rather not to poll the database as it would be wasteful. I would approach this problem by opening only one single point of entry for my data (an HTTP API, etc) and then broadcast the update to all connected clients through the SignalR Hub. Brad Wilson has a super cool presentation which demonstrate this approach:
Brad Wilson - Microsoft’s Modern Web Stack, Starring ASP.NET Web API
Here is a code sample for this approach which uses ASP.NET Web API technology for data entry. It uses in-memory dictionary for data store but the data storage technique is not the concern here:
// This hub has no inbound APIs, since all inbound communication is done
// via the HTTP API. It's here for clients which want to get continuous
// notification of changes to the ToDo database.
[HubName("todo")]
public class ToDoHub : Hub { }
public abstract class ApiControllerWithHub<THub> : ApiController
where THub : IHub {
Lazy<IHubContext> hub = new Lazy<IHubContext>(
() => GlobalHost.ConnectionManager.GetHubContext<THub>()
);
protected IHubContext Hub {
get { return hub.Value; }
}
}
public class ToDoController : ApiControllerWithHub<ToDoHub> {
private static List<ToDoItem> db = new List<ToDoItem> {
new ToDoItem { ID = 0, Title = "Do a silly demo on-stage at NDC" },
new ToDoItem { ID = 1, Title = "Wash the car" },
new ToDoItem { ID = 2, Title = "Get a haircut", Finished = true }
};
private static int lastId = db.Max(tdi => tdi.ID);
// Lines removed for brevity
public HttpResponseMessage PostNewToDoItem(ToDoItem item) {
lock (db) {
// Add item to the "database"
item.ID = Interlocked.Increment(ref lastId);
db.Add(item);
// Notify the connected clients
Hub.Clients.addItem(item);
// Return the new item, inside a 201 response
var response = Request.CreateResponse(HttpStatusCode.Created, item);
string link = Url.Link("apiRoute", new { controller = "todo", id = item.ID });
response.Headers.Location = new Uri(link);
return response;
}
}
// Lines removed for brevity
}
The full source code for the application which Brad demoed is also available: https://github.com/bradwilson/ndc2012.
The other option, which you don't prefer, is make your database to fire notifications as soon as data is changed. Then, you can pick that up and broadcast it through SignalR. Here is an example:
Database Change Notifications in ASP.NET using SignalR and SqlDependency
Sorry that this solution is not signalR, but maybe you can get ideas from it.
Here is the full example for download on GitHub