Way to get updates from database with SignalR (real time)? - c#

Is there a way to get database's exist fields and new fields with SignalR?
I would like to make a multi chat application with MVC; and I would like to store all messages etc. in sql server database...
Planned rotation of the data will be client=>sql server=>client...
The technology is new or unknown so I couldn't find smart examples...

check this article and you should be able to save all incomming messages to DB on OnReceivedAsync Method.
http://visualstudiomagazine.com/articles/2013/01/22/build-a-signal-r-app-part-1.aspx
if you need to send last 20 records to any specific user or broadcast you can use below statement once you fetched last 20 records from you DB.
Connection.Broadcast(chatData); // this will broadcast data to all connected clients.
protected override Task OnConnectedAsync(IRequest request, string connectionId)
{
_clients.Add(connectionId, string.Empty);
ChatData chatData = new ChatData("Server", "A new user has joined the room.");
return Connection.Broadcast(chatData);
}

Related

Find SignalR client by ID in its context

So I want to send a message to a specific client via SignalR. That client is not Clients.Caller - currently I can only identify it by let's call it "ID", a property in the context: this.Context.Items["ID"]
So to find a client by its ID, how do I...access all clients or contexts? Or should I save this ID in a different way? This is not the connection ID, it is an ID that maps to something in the database.
Basically I'd like to go Clients.Single(c => c.Items["ID"] == specificId).SendMessage(msg);
In the OnConnectedAsync method in your hub, you can group a user by their "ID" each time they connect.
E.G:
public async override Task OnConnectedAsync()
{
var currentUserId = /*get your current users id here*/
await Groups.AddToGroupAsync(Context.ConnectionId, $"user-{currentUserId}");
await base.OnConnectedAsync();
}
Then when you need to send that person a message, you can just broadcast it to their 'personal' group. (One person may have multiple connections, so the group approach work perfectly for us).
E.G:
Clients.Group($"user-{userId}").SendAsync("UserSpecificMessage", message);
When users disconnect, SignalR will handle the removing of connections from the groups automatically.
In using this approach, we do not have to unnecessarily broadcast a message to every single client with the intention of only one client filtering out the message based on the id.
You can send the ID down to the clients using context.Clients.User(...) or context.Clients.All(). Then in JavaScript, read the ID and compare it to what's on the page. If it's a match, carry out some action; else ignore.
As an example, let's say your app's processing a specific record on an edit screen. The record ID is on the page as a form field. You send a SignalR message down from C# with the ID. You do a comparison in JavaScript between the ID and the form field value; if they match, you display a toaster message, perform other processing, etc.

How to loop through multiple connection strings in a db and query them?

I don't know how to loop through my local database and add the values to this string:
string credentials = #"server = 127.0.0.1;
user id = system;
port = 3308;
Password = 975315";
https://i.imgur.com/6B4YFw7.png
I kind of need something like this:
string credentials = #"server = IDindb;
user id = IDindb;
port = Portindb;
Password = Pwindb";
foreach(rowofdata in localdb)
{
MySqlConnection con = new MySqlConnection(credentials);
con.Open(); //Opens the connection
MySqlCommand cmd = new MySqlCommand(sqlQuery, con); //Sends the
query to "show slave status"
Create a new table in browser that shows if server is running or not.
}
This is quite a good homework assignment - It touches frontend, database, networking and middleware, and importantly it forces you to properly think through all the little decisions of what might go wrong, e.g.
what do you do when the server doesn't respond (write "server down
into your html table)
how do you trap this without your own code failing.
What if the servers take a really long time to respond? Timeout?
Should you be doing this to each server in succession, or
all at once, adding their responses into the table as you go?
What I'm saying is you should think about your overall solution some more before diving into the code.
To answer the question, assuming you have some code to connect to the local db and query the table, rowofdata will be a recordset, with each row populated per the row in your image.
Extract the server, user, password etc credentials from it:
var masterHost = rowofdata["Master_Host"].ToString();
and create the connection string inside the for-loop.
var credentials = $"server={masterHost};user id={userId}; port={portId}; Password={password}";
The Create new Table in browser part will depend on which platform/language you're writing in. And in any case shouldn't be done here, unless you're intent on mixing db access with html (#Razor? classic asp?).
Store the server / status values in a list (of server object, with status etc), then subsequently use them to populate the html.
(Or build the html table and render that, and use ajax to request the status of each).
(unrelated - how do you plan on implementing the stop/start/fix button code?)

Searching for certain mail addresses: The Method "Contains" is not supported

using Microsoft.Azure.ActiveDirectory.GraphClient, I want to get twenty accounts from Azure Active Directory; given their email addresses. I could now send twenty requests to the Azure AD server, but in the meantime, my script times out. So I tried a single request:
public override IEnumerable<IDirectoryEntry> GetEntriesForMails(IEnumerable<MailAddress> emails)
{
foreach(IUser user in _connection.Client.Users.Where(x => emails.Contains(x.Mail)).FlattenPages())
{
yield return new AzureDirectoryEntry(user, this);
}
This throws the error that
"Contains" is not supported.
Is there another, supported, way to get all user accounts for twenty email addresses in a single round trip to the server?
Try the code :
List<IUser> users = activeDirectoryClient.Users.ExecuteAsync().Result.CurrentPage.ToList();
List<IUser> user1 = users.Where(u => emails.Any(e => e.Contains(u.Mail))).ToList();
As per a Microsoft employee, there is no direct solution in the library.
The best solution we could come up with is to keep a client-side lookup table that maps email adresses to ObjectIds; and update that table regularly (daily/weekly, and whenever a lookup failed).
One can then get the Objects for 20 ObjectIds from Azure AD in a single call (GetObjectsByObjectIdsAsync method).

What determines the SignalR connectionID and how to set it manually

I'm playing around with SignalR and was wandering how SignalR creates a connectionID is this based on my IP adress, the device I connect with or something else? And wether it is possible to set this ID manually. Let say I have a database with users which I give a unique number, can I use that number for the connectionID?
Kind regards
The SignalR connection Id is a generated guid. If you'd like to target users in a more meaningful way with your data, I found it useful to pass something with your client connection, such as a user Id and some other data from your database, along to the signalr hub and craft up a group based on what you provide. It should be a 1:1 mapping if you're trying to isolate users with your own identifiers.
you can do this by overriding OnConnected() in your hub, and implementing something like this, which would map the generated Id to your own. You can then target these groups (remember, 1:1, emulating an Id selector) to your liking.
public override Task OnConnected()
{
var user = new User()
{
Id = Context.QueryString["Id"]
Name = Context.QueryString["Name"]
}
Groups.Add(Context.ConnectionId, user.Id);
return base.OnConnected();
}
Mapping SignalR Users to Connections goes into more detail as well.

Finding Connection by UserId in SignalR

I have a webpage that uses ajax polling to get stock market updates from the server. I'd like to use SignalR instead, but I'm having trouble understanding how/if it would work.
ok, it's not really stock market updates, but the analogy works.
The SignalR examples I've seen send messages to either the current connection, all connections, or groups. In my example the stock updates happen outside of the current connection, so there's no such thing as the 'current connection'. And a user's account is associated with a few stocks, so sending a stock notification to all connections or to groups doesn't work either. I need to be able to find a connection associated with a certain userId.
Here's a fake code example:
foreach(var stock in StockService.GetStocksWithBigNews())
{
var userIds = UserService.GetUserIdsThatCareAboutStock(stock);
var connections = /* find connections associated with user ids */;
foreach(var connection in connections)
{
connection.Send(...);
}
}
In this question on filtering connections, they mention that I could keep current connections in memory but (1) it's bad for scaling and (2) it's bad for multi node websites. Both of these points are critically important to our current application. That makes me think I'd have to send a message out to all nodes to find users connected to each node >> my brain explodes in confusion.
THE QUESTION
How do I find a connection for a specific user that is scalable? Am I thinking about this the wrong way?
I created a little project last night to learn this also. I used 1.0 alpha and it was Straight forward. I created a Hub and from there on it just worked :)
I my project i have N Compute Units(some servers processing work), when they start up they invoke the ComputeUnitRegister.
await HubProxy.Invoke("ComputeUnitReqisted", _ComputeGuid);
and every time they do something they call
HubProxy.Invoke("Running", _ComputeGuid);
where HubProxy is :
HubConnection Hub = new HubConnection(RoleEnvironment.IsAvailable ?
RoleEnvironment.GetConfigurationSettingValue("SignalREndPoint"):
"http://taskqueue.cloudapp.net/");
IHubProxy HubProxy = Hub.CreateHubProxy("ComputeUnits");
I used RoleEnviroment.IsAvailable because i can now run this as a Azure Role , a Console App or what ever in .NET 4.5. The Hub is placed in a MVC4 Website project and is started like this:
GlobalHost.Configuration.ConnectionTimeout = TimeSpan.FromSeconds(50);
RouteTable.Routes.MapHubs();
public class ComputeUnits : Hub
{
public Task Running(Guid MyGuid)
{
return Clients.Group(MyGuid.ToString()).ComputeUnitHeartBeat(MyGuid,
DateTime.UtcNow.ToEpochMilliseconds());
}
public Task ComputeUnitReqister(Guid MyGuid)
{
Groups.Add(Context.ConnectionId, "ComputeUnits").Wait();
return Clients.Others.ComputeUnitCameOnline(new { Guid = MyGuid,
HeartBeat = DateTime.UtcNow.ToEpochMilliseconds() });
}
public void SubscribeToHeartBeats(Guid MyGuid)
{
Groups.Add(Context.ConnectionId, MyGuid.ToString());
}
}
My clients are Javascript clients, that have methods for(let me know if you need to see the code for this also). But basicly they listhen for the ComputeUnitCameOnline and when its run they call on the server SubscribeToHeartBeats. This means that whenever the server compute unit is doing some work it will call Running, which will trigger a ComputeUnitHeartBeat on javascript clients.
I hope you can use this to see how Groups and Connections can be used. And last, its also scaled out over multiply azure roles by adding a few lines of code:
GlobalHost.HubPipeline.EnableAutoRejoiningGroups();
GlobalHost.DependencyResolver.UseServiceBus(
serviceBusConnectionString,
2,
3,
GetRoleInstanceNumber(),
topicPathPrefix /* the prefix applied to the name of each topic used */
);
You can get the connection string on the servicebus on azure, remember the Provider=SharedSecret. But when adding the nuget packaged the connectionstring syntax is also pasted into your web.config.
2 is how many topics to split it about. Topics can contain 1Gb of data, so depending on performance you can increase it.
3 is the number of nodes to split it out on. I used 3 because i have 2 Azure Instances, and my localhost. You can get the RoleNumber like this (note that i hard coded my localhost to 2).
private static int GetRoleInstanceNumber()
{
if (!RoleEnvironment.IsAvailable)
return 2;
var roleInstanceId = RoleEnvironment.CurrentRoleInstance.Id;
var li1 = roleInstanceId.LastIndexOf(".");
var li2 = roleInstanceId.LastIndexOf("_");
var roleInstanceNo = roleInstanceId.Substring(Math.Max(li1, li2) + 1);
return Int32.Parse(roleInstanceNo);
}
You can see it all live at : http://taskqueue.cloudapp.net/#/compute-units
When using SignalR, after a client has connected to the server they are served up a Connection ID (this is essential to providing real time communication). Yes this is stored in memory but SignalR also can be used in multi-node environments. You can use the Redis or even Sql Server backplane (more to come) for example. So long story short, we take care of your scale-out scenarios for you via backplanes/service bus' without you having to worry about it.

Categories