SignalR - access clients from server-side business logic - signalr

I have a requirement to start a process on the server that may run for several minutes, so I was thinking of exposing the following hub method:-
public async Task Start()
{
await Task.Run(() => _myService.Start());
}
There would also be a Stop() method that allows a client to stop the running process, probably via a cancellation token. I've also omitted code that prevents it from being started if already running, error handling, etc.
Additionally, the long-running process will be collecting data which it needs to periodically broadcast back to the client(s), so I was wondering about using an event - something like this:-
public async Task Start()
{
_myService.AfterDataCollected += AfterDataCollectedHandler;
await Task.Run(() => _myService.Start());
_myService.AfterDataCollected -= AfterDataCollectedHandler;
}
private void AfterDataCollectedHandler(object sender, MyDataEventArgs e)
{
Clients.All.SendData(e.Data);
}
Is this an acceptable solution or is there a "better" way?

You don't need to use SignalR to start the work, you can use the applications already existing framework / design / API for this and only use SignalR for the pub sub part.
I did this for my current customers project, a user starts a work and all tabs belonging to that user is updated using signalr, I used a out sun library called SignalR.EventAggregatorProxy to abstract the domain from SignalR. Disclaimer : I'm the author of said library
http://andersmalmgren.com/2014/05/27/client-server-event-aggregation-with-signalr/
edit: Using the .NET client your code would look something like this
public class MyViewModel : IHandle<WorkProgress>
{
public MyViewModel(IEventAggregator eventAggregator)
{
eventAggregator.Subscribe(this);
}
public void Handle(WorkProgress message)
{
//Act on work progress
}
}

Related

ASP.NET Some service register after mediator

We are working on service which collect data from AWS SQS then send batch to client. We are using mediator to publish notifications. The diagram of program looks like:
The problem is in first NotificationHandler from Mediatr.
private readonly EventCollectorHostedService _collector;
public CollectIncomingEventNotificationHandler(EventCollectorHostedService collector)
{
_collector = collector;
}
Class EventCollectorHostedService is register after Mediator so is not visible during registering this NotificationHandler and additionally it use Mediator to publish notification that batch is ready to send.
The error is that cannot construct CollectIncomingEventNotificationHandler because -> Unable to resolve service for type 'Api.Services.HostedServices.EventCollectorHostedService'.
services.AddMediatR(typeof(Startup).GetTypeInfo().Assembly);
services.AddHostedService<EventCollectorHostedService>();
The ugly solution is to declare some functionality in EventCollectorHostedService as static or instead of injecting EventCollectorHostedService, inject IServiceProvider.
But these solution don't look clean for me so do you have any other better solution ?
Thanks in advance.
Maybe someone encountered with similar problem so finally i have a brilliant solution.
Background services have to be treat like separate microservices based on event driven architecture so we have to make internal message broker mechanism.
The very simple solution which cover my case is:
public class NotificationChannel : INotificationChannel
{
public event EventHandler<IncomingEventNotificataionEventArgs> IncomingEventReceived;
public void Publish<T>(T notification)
{
if(notification is IncomingEventNotification incomingEventNotification)
{
OnIncomingEventReceived(incomingEventNotification);
}
}
protected virtual void OnIncomingEventReceived(IncomingEventNotification notification)
{
if(IncomingEventReceived != null)
{
var args = new IncomingEventNotificataionEventArgs(notification);
IncomingEventReceived(this, args);
}
}
}

Rebus - Run unit tests and wait for Rebus to complete all threads

I am building an integration test where I am using InMemNetwork to run the test.
There is a Thread.Sleep call just before an assert but that is a dodgy way of testing and it slows down our tests a lot.
I am also doing some integration tests using SagaFixtures and a simple IBus implementation that runs synchronously but its all a bit tedious with registering handlers, running handlers and deferring messages.
Is there a way to wait on all threads in use by Rebus until they are finished executing without augmenting production code using things like ManualResetEvent (used in Rebus own tests)?
I usually use SagaFixture as you do, and then I use FakeBus to inject into saga handlers in order to capture their actions.
Most of my tests are unit tests of simple handlers though, but I will often inject "real" service, like e.g. implementation of IThis and IThat that go to a real database.
For a couple of scenarios though I spin up multiple endpoints with an in-mem transport, and then I usually implement an extension on InMemNetwork that helps me wait for particular events to be published or something like that – it could look like this in a test:
var updated = await Network.WaitForNext<WhateverUpdated>(subscriberAddress, timeoutSeconds: 20);
where WaitForNext is simply an extension method that polls the queue specified by subscriberAddress for the next message and tries to deserialize it as WhateverUpdated.
I hope that can give you some inspiration :)
For some scenario's I use the following approach to wait for Rebus to complete all message processing. The rebus endpoints are hosted in separate exe's and the rebus filesystem transport is used for integration tests (normally it's Azure SB). The integration test spins up the exe's and in each exe Rebus is configured with 0 workers, so it's doing nothing. Then in the test we have a WaitForMessagesProcessed() method that configures a number of workers and blocks until there are no more messages to be processed.
Here is how it roughly looks in code:
public class MessageProcessor() {
private string queueName;
private int messagesWaitingForProcessing;
private IBus bus;
public MessageProcessor(string queueName) {
this.queueName = queueName;
this.bus = Configure.With(adapter)
.Transport(t => t.UseFileSystem(#"c:\baseDirectory", this.queueName))
.Options(o =>
{
o.SetNumberOfWorkers(0);
})
.Events(e =>
{
e.BeforeMessageSent += (thebus, headers, message, context) =>
{
// When sending to itself, the message is not queued on the network.
var m = context.Load<Rebus.Pipeline.Send.DestinationAddresses>();
if (m.Any(t => t == this.queueName))
this.messagesWaitingForProcessing++;
};
e.AfterMessageHandled += (thebus, headers, message, context, args) =>
{
this.messagesWaitingForProcessing--;
};
})
.Start();
}
public async Task WaitForMessagesProcessed()
{
this.DetermineMessagesWaitingForProcessing();
while (this.messagesWaitingForProcessing > 0)
{
this.bus.Advanced.Workers.SetNumberOfWorkers(2);
while (this.messagesWaitingForProcessing > 0)
{
await Task.Delay(100);
}
this.bus.Advanced.Workers.SetNumberOfWorkers(0);
this.DetermineMessagesWaitingForProcessing();
}
}
public void DetermineMessagesWaitingForProcessing() {
this.messagesWaitingForProcessing = Directory.GetFiles(GetDirectoryForQueueNamed(this.queueName), "*.rebusmessage.json").Count();
}
private static string GetDirectoryForQueueNamed(string queueName)
{
return Path.Combine(this.baseDiretory, queueName);
}
}
A test could be like
[TestMethod]
public void Test() {
var endpoint1 = LaunchExe("1");
var endpoint2 = LaunchExe("2");
endPoint1.DoSomeAction();
endPoint1.WaitForMessagesProcessed();
Assert.AreEqual("expectation", endPoint1.Query());
}

Is it possible to make a background task with SignalR

I am using Microsoft SignalR in order to push notification to browsers. Those notifications are triggered by action from other browsers. I want to make a background task which send notification sometimes. For example, at 12:45:21 i want to fire a notification to all connected users, even if they are doing nothing. Is it possible to do that ?
SignalR doesn't give you the ability to run a background task, but if you run are running a background task, there is nothing to stop your task using your SignalR hub to invoke client methods and send any desired notification.
To launch and control your background task, Hangfire is a flexible library that should help.
Edit to add: Since you've clarified you want to do this in a windows service, another prominent library to assist with building and deploying services is TopShelf
Edit to add: Also, I gather from your comment that you're trying to understand how to access the hub object from your background task? There are many ways to do this, but to improve testability and maintainability of your program, I recommend using an IoC (Inversion of Control) container, and injecting the necessary references - this tutorial: Dependency Injection in SignalR has a walkthrough using the Ninject library. That walkthrough is oriented towards asp.net hosting, but the link you found should help with adapting to a windows service.
If you are using asp.net core 2.1, this is now possible using BackgroundService/IHostedService
https://github.com/davidfowl/UT3/blob/fb12e182d42d2a5a902c1979ea0e91b66fe60607/UTT/Scavenger.cs#L9-L40
(Contents below)
public class Scavenger : BackgroundService
{
private readonly IHubContext<UTTHub> _hubContext;
private readonly ILogger<Scavenger> _logger;
public Scavenger(IHubContext<UTTHub> hubContext, ILogger<Scavenger> logger)
{
_hubContext = hubContext;
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
// Mark games that haven't played in a while as completed
var changed = Game.MarkExpiredGames();
// Mark completed games as removed
var removed = Game.RemoveCompletedGames();
if (removed > 0)
{
_logger.LogInformation("Removed {GameCount} games.", removed);
}
if (removed > 0 || changed)
{
await _hubContext.Clients.All.SendAsync("GameUpdated", Game.GetGames());
}
await Task.Delay(5000);
}
}
}
}
Also see this
https://github.com/aspnet/Docs/issues/8925

Get names of Online users connected to a Server

I am new to asp.net. I have gone through this link which has shown how to count the online users connected to a server using asp.net. (which is working when I tried)
My question is: What should I change in that code (Global.asax) so that It shows all the names of the connected users instead of counting them.
I created a chat application which stores the name of the connected user in a variable chatUsername in js file as shown below:
js file
var chatUsername = window.prompt("Enter Username:", "");
//
chat.client.addMessage = //Function
//
chat.server.send(chatUsername);
.aspx.cs file
//Using SignalR (I think this doesnt matter)
public class Chat : Hub
{
public void Send(string from)
{
// Call the addMessage method on all clients
Clients.All.addMessage(from);
}
}
You can find my complete code here
EDIT: Please provide a simple example related only to asp.net or signalr (no other technologies like MVC)
Please help.
Edit: following code refers to SignalR v0.5, not the latest 1.0Alpha2, but I believe the reasoning is the same
To do this you need to add several steps to your SignalR connection process, both in the server and in the client:
on the server side:
on application start-up, for example, you can instantiate a static in-memory repository (can be a dictionary of ) that will serve as the user repository to store all currently connected users.
In the hub you need to handle the Disconnect event (when a user disconnects, needs to be removed from the user repository as well) and notify all other clients that this user disconnected
In the hub you need to add two new methods (the names can be whatever you want) that will help client connect to the system and get the list of currently connected users:
GetConnectedUsers() that just returns a collection of connected users
Joined() where the Hub will create a new User, using the info stored in the round-trip state (the username selected by the client) and the SignalR connection ID, and add the newly created user to the in-memory repository.
on the client side:
First you need to instantiate the javascript object that relates to your server-side hub
var chat = $.connection.chat;
chat.username = chatUsername;
Then implements all the functions that will be called by the hub and finally connect to the hub:
// Step 1: Start the connection
// Step 2: Get all currenlty connected users
// Step 3: Join to the chat and notify all the clients (me included) that there is a new user connected
$.connection.hub.start()
.done(function () {
chat.getConnectedUsers()
.done(/*display your contacts*/);
});
}).done(function () {
chat.joined();
});
});
});
If you are asking why we need to add a stage like "chat.joined()" is because in the method on the Hub that is handling the connection event, the round-trip state is not yet available, so the hub cannot retrieve the username chosen by the user.
Anyway I made a blog post to show more in detail how to create a basic SignalR chat web application using Asp.Net MVC, and it is available at:
http://thewayofcode.wordpress.com/2012/07/24/chatr-just-another-chat-application-using-signalr/
In the post you will also find a link to the github repository where the source is published.
I hope this helps.
Valerio
Apparently, you are using Signal-R - so try tracking state of online users (i.e. connected clients) in java-script itself. Use Connected/Disconnected/Reconnected server side events to broadcast to all clients - from documentation:
public class Chat : Hub
{
public override Task OnConnected()
{
return Clients.All.joined(Context.ConnectionId, DateTime.Now.ToString());
}
public override Task OnDisconnected()
{
return Clients.All.leave(Context.ConnectionId, DateTime.Now.ToString());
}
public override Task OnReconnected()
{
return Clients.All.rejoined(Context.ConnectionId, DateTime.Now.ToString());
}
}
A global server side store (for example - a static dictionary) can be used to store state against the connection id - that way, this dictionary can give you users for needed connection ids. For example,
// dis-claimer: untested code - just to give the idea/hint/outline
public class Chat : Hub
{
// change to use Concurrent Dictionary (or do thread-safe access)
static Dictionary<string, User> _users = new Dictionary<string, User>()
// call from client when it goes online
public void Join(string name)
{
var connId = this.Context.ConnectionId;
__users.Add(connId, new User(connId, name));
}
public override Task OnConnected()
{
return Clients.All.joined(_users[Context.ConnectionId], DateTime.Now.ToString());
}
public override Task OnDisconnected()
{
var user = _users[Context.ConnectionId];
_users.Remove(Context.ConnectionId);
return Clients.All.leave(user, DateTime.Now.ToString());
}
public List<User> GetUsers()
{
return _users.Values.ToList()
}
}
I think this should work for you :-
void Application_Start(object sender, EventArgs e)
{
// Code that runs on application startup
Application["OnlineUsers"] = 0;
List<string> list = new List<string>();
}
//First check if it is Authenticated request:-
void Session_Start(object sender, EventArgs e)
{
if(Request.IsAuthenticated)
list.Add(User.Identity.Name);
//your rest of code .......
}
list will return you all the username who are online :-

ASP.net how to long polling with PokeIn?

I want to make a service that notify the user in case there are some new messages sent to him. Thus I want to use some Comet framework that provide the server push ability. So I have looked into PokeIn.
Just wondering a thing. I have checked on the samples that they have on the website. None of them look into the database to retrieve new entries if there are some. But it is just a matter of modification to it I guess.
One of the sample implement this long polling by using a sleep on the server side. So if I use the same approach I can check the database, if there are any new entries, every 5 seconds. However this approach doesn't seem to be much different from when using polling on the client side with javascript.
This part is from a sample. As can be seen they put a sleep there for to update current time for everybody.
static void UpdateClients()
{
while (true)
{
//.. code to check database
if (CometWorker.ActiveClientCount > 0)
{
CometWorker.SendToAll(JSON.Method("UpdateTime", DateTime.Now));
}
Thread.Sleep(500);
}
}
So I wonder is this how I should implement the message notifier? It seems that the above approach is still going to push a huge load demand on the server side. The message notifier is intend to work same way as the one found Facebook.
You shouldn't implement this way, that sample is only implemented like that because the keep PokeIn related part is clear. You should implement SQL part as mentioned http://www.codeproject.com/Articles/12335/Using-SqlDependency-for-data-change-events
in order to track changes on database.
So, when you have something to send, call one of the PokeIn methods for the client side delivery. I don't know, how much your application is time critical because in addition to reverse ajax, PokeIn's internal websocket feature is very easy to activate and delivers messages to client quite fast.
You can do this with database as #Zuuum said, but I implemented it in a different way.
I'm using ASP.NET MVC with PokeIn and EF in a Windows Azure environment:
I have domain events similar to this approach: Strengthening your domain: Domain Events
When someone invokes an action, that's a Unit of Work
If that UOW succeeds then I raise a domain event (e.g. ChatMessageSent)
I have subscribers for these domain events so they can receive the event and forward the message to the PokeIn listeners
I use this pattern for all my real-time needs on my game site (making moves, actions etc in a game), I don't want to advertise it here, you can find it through me if you want.
I always use this pattern as a duplex communication solution so everybody gets their update via PokeIn, even the user who invoked the action so every client will behave the same. So when someone calls an action it won't return anything except the success signal.
The next examples are won't work because they are only snippets to demonstrate the flow
Here is an action snippet from my code:
[HttpPost]
[UnitOfWork]
[RestrictToAjax]
[ValidateAntiForgeryToken]
public JsonResult Post(SendMessageViewModel msg)
{
if (ModelState.IsValid)
{
var chatMessage = new ChatMessage
{
ContainerType = msg.ContainerType,
ContainerID = msg.ContainerID,
Message = _xssEncoder.Encode(msg.Message),
User = _profileService.CurrentUser
};
_chatRepository.AddMessage(chatMessage);
OnSuccessfulUoW = () => EventBroker.Current.Send(this, new ChatMessageSentPayload(chatMessage));
}
return Json(Constants.AjaxOk);
}
And the (simplified) EventBroker implementation:
public class UnityEventBroker : EventBroker
{
private readonly IUnityContainer _container;
public UnityEventBroker(IUnityContainer container)
{
_container = container;
}
public override void Send<TPayload>(object sender, TPayload payload)
{
var subscribers = _container.ResolveAll<IEventSubscriber<TPayload>>();
if (subscribers == null) return;
foreach (var subscriber in subscribers)
{
subscriber.Receive(sender, payload);
}
}
}
And the even more simplified subscriber:
public class ChatMessageSentSubscriber : IEventSubscriber<ChatMessageSentPayload>
{
public void Receive(object sender, ChatMessageSentPayload payload)
{
var message = payload.Message;
var content = SiteContent.Global;
var clients = Client.GetClients(c => c.ContentID == message.ContainerID && c.Content == content)
.Select(c => c.ClientID)
.ToArray();
var dto = ObjectMapper.Current.Map<ChatMessage, ChatMessageSentDto>(message);
var json = PokeIn.JSON.Method("pokein", dto);
CometWorker.SendToClients(clients, json);
}
}

Resources