SignalR .NET Client not receiving messages - c#

I have a .NET console application that I am practicing signalR with.
var hubConnection = new HubConnection("http://URL/signalr/");
var hub = hubConnection.CreateHubProxy("Hub");
hub.StateChanged += change =>
{
Console.WriteLine(change.NewState);
};
hub.Received += s =>
{
Console.WriteLine(s);
};
hub.On<string, string>("processMessage", (group, message) =>
{
Console.WriteLine(message);
});
await hubConnection.Start();
await hub.Invoke<string>("Subscribe", "New group");
I see the state changing from Connecting to Connected but I am not getting a "Received" event on the client when the server sends a message. The server is sending a group message as soon as the client subscribes and I can see the message being sent with the correct "New group" groupname, however I never receive the message on the client. I also do not receive the processMessage event when the server uses that method.
Server Code
private void CallBack(string group, string message)
{
Clients.Group(group).processMessage(group, message);
}
The other method on the server is Subscribe which just sets my inner server to use the CallBack method when it has data available to send to the client.
Edit
This works in Javascript it just doesn't seem to work in the .NET client.

Without full serverside code it's hard to say but I think this part is wrong
hubConnection.CreateHubProxy("Hub");
as argument you need the name of your hubclass on serverside. For example
hubConnection.CreateHubProxy("MyHub");
To get more informations on clientside why it fails you can temporary add the following to your HubConnection
hubConnection.TraceLevel = TraceLevels.All;
hubConnection.TraceWriter = Console.Out;
After adding this you will get further debuging informations in your output section in VS

Related

Azure Service Bus send message every other time

I've a c# dotnet webjob and a simple desktop app.
Sending a message apperaes to work only every other time.
serviceBusClient = new QueueClient(_config["ServiceBusConnectionString"], "queuename", ReceiveMode.ReceiveAndDelete);
await serviceBusClient.SendMigrationMessageAsync("1", label);
await serviceBusClient.SendMigrationMessageAsync("2", label);
await serviceBusClient.SendMigrationMessageAsync("3", label);
await serviceBusClient.SendMigrationMessageAsync("4", label);
SendMigrationMessageAsync is an extension:
public static async Task SendMigrationMessageAsync(this IQueueClient client, string messageText, string label)
{
Message message = new Message(Encoding.UTF8.GetBytes(messageText));
message.Label = label;
await client.SendAsync(message);
}
In the destkop app I registered to receive the message and also registered a message exception handler (which is not call at all).
In this scenario I can only receive message "2" and "4".
When I stopped execution after the first message had been sent, the message never showed up on the Azure service.
Thanks in advance
EDITED:
I found out that arter creating brand new Azure Service Bus Namespace, all is working fine.
I had basic pricing tier and even after upgrading to standard I was able to only send every other message.
Creating new service sorted this out.
Is there any limitation or throtling? I haven't sent many messages at all, something around 300 daily.
You most probably had two processes with the same subscription id, so they are "stealing" messages from each other. Let's say there are two console apps, the first one sending messages and the second one receiving.
With both having same subscription id it looks like this:
And with the unique subscription for each process everything is ok:

why Firebase in C# form app not realtime?

I'm trying to build an app that detects product from android and send it in real-time to the desktop application via firebase but I don't know firebase doesn't work in real-time when I get value on C# app (I mean when I change values it doesn't change in the app automatically.)
Rq: I'm using FireSharp reference and My code for receiving:
public void getdata()
{
var res = client.Get(#"Store/");
Store str = res.ResultAs<Store>();
store.Text = str.Tags;
nb.Text = str.nb;
}
By calling the Get method, you're calling the server to get the data only once.
If you want to instead get the data now and then continue to listen for updates, use the OnAsync method. From the linked documentation:
EventStreamResponse response = await _client.OnAsync("chat", (sender, args, context) => {
System.Console.WriteLine(args.Data);
});
//Call dispose to stop listening for events
response.Dispose();

Push notifications with SignalR

I am developing an application in .net core with SignalR. Users will be subscribed to the system. What I need to know is: Does a user have to be logged in to receive a notification? I would like for the notification to be pushed without them having to log in every time. It must be similar to a WhatsApp message that just "arrives". Is this possible with SignalR?
Each active browser tab is one connection to SignalR (the Client), with a unique ConnectionId. Depending on the usage of notifications, a visitor does not have to be logged in. A connection with a SignalR Hub is established when the JavaScript code has been initialized.
You can simply invoke (call) a JavaScript function from the server for each Client (visitor). So all visitors will receive the notification:
await Clients.All.SendAsync("ReceiveNotification", "Your notification message");
All connected clients will receive this 'event' from the server. Write a 'listener' for the ReceiveNotification event inside your JavaScript to do something client side:
connection.on("ReceiveNotification", function (user, message) {
// Show the notification.
});
Example
Depending on how you want to send a notification, you can either invoke the ReceiveNotification:
From the JavaScript;
connection.invoke("SendMessage", user, message).catch(function (err) {
return console.error(err.toString());
});
From the Server (e.g. a controller), using IHttpContext<THub>
public class HomeController : Controller
{
private readonly IHubContext<SomeHub> _hubContext;
public HomeController(IHubContext<SomeHub> hubContext)
{
_hubContext = hubContext;
}
public async Task<IActionResult> Index()
{
await _hubContext.Clients.All.SendAsync("ReceiveNotifiction", "Your notification message");
return View();
}
}
Example (modified) is taken from the SignalR HubContext documentation.

Resume Bot Framework dialog when triggered by external service

The Scenario
I have a bot built using the Bot Framework with a series of dialogs. One of these dialogs gives the user the option of inputting some complex data via a web page by presenting a button to them. Clicking the button they are then taken to the site, fill out the data, save and are then directed back to the bot.
I want my bot to pause the dialog until it receives an event from my web page telling me the user has saved the data and then continue asking the user questions.
Before
I had a version implemented whereby I would store a ConversationReference before the user clicked the button and then when the external event happened I would send the cards and next messages I wanted to show (not in a dialog) from a webhook, that was fine but it got quite complicated/messy - I'd rather keep the whole app in one continuous dialog.
Idea 1: Use DirectLine API
I did some research and many people were suggesting using the DirectLine API. So I implemented this:
public async Task SendEventAsync(InternalEventMessage message, ConversationReference reference) {
var client = new DirectLineClient(!String.IsNullOrEmpty(_settings.DirectLineSecret) ? _settings.DirectLineSecret : null);
if (_settings.SiteUrl.Contains("localhost")) {
client.BaseUri = new Uri(_settings.DirectLineServiceUrl);
}
var eventMessage = Activity.CreateEventActivity();
//Wrong way round?!?
eventMessage.From = reference.Bot;
eventMessage.Type = ActivityTypes.Event;
eventMessage.Value = message;
var conversation = await client.Conversations.PostActivityAsync(reference.Conversation.Id, eventMessage as Activity);
}
This uses the DirectLine client to send an event message to the serviceUrl using a stored ConversationReference, basically imitating a user (bot and user seem to be the wrong way round in the SDK). Checking for localhost was so that the DirectLine library pointed at the emulator server rather than https://directline.botframework.com.
In my dialog I call:
//method above shows input button and links to web page
context.Wait(WaitForAddressInput);
}
private async Task WaitForAddressInput(IDialogContext context, IAwaitable<IActivity> result) {
var message = await result;
switch (message.Type) {
case ActivityTypes.Message:
//TODO: Add response
break;
case ActivityTypes.Event:
var eventMessage = message as IEventActivity;
if (((JObject)eventMessage.Value).ToObject<InternalEventMessage>().Type == EventType.AddressInputComplete) {
_addressResult = (await _tableService.ReadOrderById(Order.OrderId)).Address;
await context.PostAsync($"Great}");
context.Done(_addressResult);
}
break;
}
}
This waits for any message from the user after the button has been shown and if our event matches then we proceed with the dialog.
This works locally using the emulator but, frustratingly, doesn't live. It fails to recognise channels created via webchat or Messenger. That is explained here: Microsoft Bot Framework DirectLine Can't Access Conversations
For security reasons, you can't use DirectLine to spy on messages from
another conversation.
So I can't access a channel that I haven't created using DirectLine.
Idea 2: BotConnector
So I thought I'd try the BotConnector using similar code:
public async Task SendEventAsync(InternalEventMessage message, Microsoft.Bot.Connector.DirectLine.ConversationReference reference) {
var botAccount = new ChannelAccount(reference.User.Id, reference.User.Name);
var userAccount = new ChannelAccount(reference.Bot.Id, reference.Bot.Name);
MicrosoftAppCredentials.TrustServiceUrl(reference.ServiceUrl);
var connector = new ConnectorClient(new Uri(reference.ServiceUrl), new MicrosoftAppCredentials("xxxxxxxxxxxxxxxxxxxxxxxx", "xxxxxxxxxxxxxxxxxxxxxxxx"));
connector.Credentials.InitializeServiceClient();
var eventMessage = Activity.CreateMessageActivity();
eventMessage.Recipient = botAccount;
eventMessage.From = userAccount;
eventMessage.Type = ActivityTypes.Event;
eventMessage.Conversation = new ConversationAccount(id: reference.Conversation.Id);
eventMessage.ServiceUrl = reference.ServiceUrl;
eventMessage.Timestamp = DateTimeOffset.UtcNow;
eventMessage.LocalTimestamp = DateTime.Now;
eventMessage.ChannelId = reference.ChannelId;
var result = await connector.Conversations.SendToConversationAsync(eventMessage as Microsoft.Bot.Connector.Activity);
}
This doesn't crash and I can see the event appear in the emulator request console but nothing happens, it seems to be ignored!
Idea 3: Try to imitate the bot service calling my bot
I haven't tried this yet because I think it might be the most time consuming but I was reading here about the service authentication and wondered if it would be possible to imitate the hosted bot service sending a message and send my event that way with the required data?
This seems like a fairly common scenario so I'm surprised I haven't come across a way to do this yet. If anyone has any other ideas on how I can send an event message to my bot from an external service then I'd love to hear it.
Update:
See my answer below Eric's to see what I did.
Idea 1:
DirectLine is a channel, not a library to use in order to connect to channels. (For instance: you would not use Facebook Messenger to connect to Skype) DirectLineClient is useful for creating a client application that connects to the DirectLine channel through the Direct Line connector service.
Idea 2:
This method should work. In fact, the BotAuth library uses this method for the MagicNumber login flow within the CallbackController: https://github.com/MicrosoftDX/botauth/blob/9a0a9f1b665f4aa95b6d60d09346dda90d8b314e/CSharp/BotAuth/Controllers/CallbackController.cs
For your scenario, you should be able to construct a CardAction of type ActionTypes.OpenUrl that contains a value with the ConversationReference encoded in the url. Clicking the button will call an mvc controller that displays a page (saving the ConversationReference in a cookie or something) and when the user finishes adding the address on the page, use the ConversationReference to send an event to the bot (similar to how BotAuth resumes the conversation in the CallbackController).
Idea 3:
This would bypass the connector services, and is not a supported scenario. The link you shared explains the details of how authentication works in the Bot Framework, not how to bypass the connector services.
Eric's answer led me to solve the issue using the BotAuth example but, for completeness, here is what I did using Idea 2.
I created a CallbackController on my Bot Framework endpoint and then used the following code to send an event back to the awaiting dialog:
MicrosoftAppCredentials.TrustServiceUrl(reference.ServiceUrl);
var message = reference.GetPostToBotMessage();
message.Value = new InternalEventMessage(type);
message.Type = ActivityTypes.Event;
await Conversation.ResumeAsync(reference, message);
The dialog awaits with this code and continues:
context.Wait(WaitForAddressInput);
}
private async Task WaitForAddressInput(IDialogContext context,
IAwaitable<IActivity> result)
{
var message = await result;
switch (message.Type)
{
case ActivityTypes.Message:
//TODO: Add response
break;
case ActivityTypes.Event:
//Process event and continue!
break;
}
}
This is the most complicated issue I've had with the Bot Framework and I found the docs a little lacking. Hope this helps someone!

SignalR Send method not firing while trying to poll for existing connections

I'm using the latest version of SignalR with jQuery and getting some odd behavior where a user disconnects, but still hangs around in my "UserQueue" despite having disconnected.
I think this may be related to the fact that a page refresh appears to trigger the OnDisconnected and OnConnected events almost simultaneously. When I set a break point in one of these methods and step through, my pointer bounces back and forth between the two methods with each step (F10).
I'd like to run a check with each OnConnected event to find out who is actually connected to my app. I want to fire a JS event from my C# code in the OnConnected event, and then allow the client/front end to fire back a confirmation of the user being present:
public override Task OnConnected()
{
// Check for other users:
Clients.All.checkForOtherUsers();
// Do stuff with my user queue
var curmodelId = GetModelId(Context);
var curUserConnection = GetCurrentUser(Context);
// First create the ledger is missing, setting the modelId as the first key
if (_ledger == null)
{
_ledger = new ConnectionLedger(curmodelId, curUserConnection);
}
else
{
// key not found, create new connection pool this model
if (!_ledger.PoolIds.Contains(curmodelId))
{
_ledger.AddConnectionPool(curmodelId, curUserConnection);
}
else
{
var curModelPool = _ledger.ConnectionPools.First(x => x.Id == curmodelId);
curModelPool.UserQueue.Enqueue(curUserConnection);
}
}
return base.OnConnected();
}
Now in the client JS, if I have this code:
modelHub.client.checkForOtherUsers = function () {
// I can see logging here if I add console.log("checking for other users...")
modelHub.server.send();
}
...I'm expecting my C# Send method to receive back the context (if the user is actually present at the client for the JS to execute) and update my UserQueue:
public void Send()
{
var curmodelId = GetModelId(Context);
var curModelPool = _ledger.ConnectionPools.First(x => x.Id == curmodelId);
var curUserConnection = GetCurrentUser(Context);
curModelPool.UserQueue.Enqueue(curUserConnection);
}
... but my Send method never gets fired, even when I fire it from the JS Console directly $.connection.modelingHub.server.send().
Why doesn't the Send method fire? Is there something about the order of events / async nature of the SignalR library that causes this?
Comments on the overall design are welcome too (since I suspect it could be stupid), but note that I cannot access Context.User or Clients.User because of my customized Authentication config.
Seems to be two causes below.
Server-side
Not good to call Clients.All.checkForOtherUsers(); within OnConnected().
I would recommend calling it after connected.
Please refer to SignalR - Send message OnConnected
Client-side
Might need to regist modelHub.client.checkForOtherUsers = function () { before calling start method.
Normally you register event handlers before calling the start method
to establish the connection. If you want to register some event
handlers after establishing the connection, you can do that, but you
must register at least one of your event handler(s) before calling the
start method. - How to establish a connection

Categories