I am new to SignalR and am experimenting with setting up a self host service that I can use to send events to other applications. I am having a problem getting a method to be called on all clients. In the code below _context.Clients.All is always empty. Any Ideas?
Test Method:
var server = new SignalRServer();
server.Start("http://localhost:8555");
var hubConnection = new HubConnection("http://localhost:8555");
var proxy = hubConnection.CreateHubProxy("testHub");
var executed = false;
proxy.On("testMethod", () =>
{
executed = true;
});
hubConnection.Start().Wait();
var hub = new TestHubContext(GlobalHost.ConnectionManager.GetHubContext<TestHub>());
hub.Test();
hubConnection.Stop();
server.Stop();
Self host server:
public class SignalRServer
{
private IDisposable _signalR;
public void Start(string url)
{
_signalR = WebApp.Start<SignalRStartup>(url);
}
public void Stop()
{
_signalR.Dispose();
}
}
public class SignalRStartup
{
public static IAppBuilder App = null;
public void Configuration(IAppBuilder app)
{
app.UseCors(CorsOptions.AllowAll);
app.MapSignalR();
}
}
Hub:
public class TestHub : Hub
{
}
public class TestHubContext
{
private IHubContext _context;
public TestHubContext(IHubContext context)
{
_context = context;
}
public void Test()
{
if (_context != null)
{
// At this poing _context.Clients.All is always empty
_context.Clients.All.testMethod();
}
}
}
I think your context / client connections are fine. Without further information I'm guessing your problem is that you are closing your connection and server too quickly after calling hub.Test()
Comment out the two .Stop() calls (and stick a Console.ReadLine in there to keep the console open) and see what happens
Related
I'm a newb to SignalR. I'm trying to set up a Asp.Net Core WebAPI so that other clients can connect to it using SignalR and get real-time data.
My Hub class is:
public class TimeHub : Hub
{
public async Task UpdateTime(string message)
{
await Clients.All.SendAsync("ReceiveMessage", message);
}
}
I have a relay class as follows:
public class TimeRelay : ITimeRelay
{
private readonly IHubContext<TimeHub> _timeHubContext;
public TimeRelay(IHubContext<TimeHub> context)
{
_timeHubContext = context;
Task.Factory.StartNew(async () =>
{
while (true)
{
await context.Clients.All.SendAsync("UpdateTime", DateTime.Now.ToShortDateString());
Thread.Sleep(2000);
}
});
}
}
Startup class:
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddSignalR();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseDeveloperExceptionPage();
app.UseHttpsRedirection();
app.UseSignalR((x) =>
{
x.MapHub<TimeHub>("/timeHub");
});
app.UseMvc();
}
The client is a console application and the code is:
class Program
{
static Action<string> OnReceivedAction = OnReceived;
static void Main(string[] args)
{
Connect();
Console.ReadLine();
}
private static async void Connect()
{
var hubConnectionBuilder = new HubConnectionBuilder();
var hubConnection = hubConnectionBuilder.WithUrl("http://localhost:60211/timeHub").Build();
await hubConnection.StartAsync();
var on = hubConnection.On("ReceiveMessage", OnReceivedAction);
Console.ReadLine();
on.Dispose();
await hubConnection.StopAsync();
}
static void OnReceived(string message)
{
System.Console.WriteLine($"{message}");
}
}
I tried debugging the application. The client got connected to the TimeHub succesfully. The no of connections in Clients.All changed from 0 to 1, when the client got connected. But, when await context.Clients.All.SendAsync("UpdateTime", DateTime.Now.ToShortDateString()); is executed, the UpdateTime function in TimeHub is not getting executed and the client is not getting any message.
I tried using "UpdateTime", "SendMessage", and "ReceiveMessage" as method in Clients.All.SendAsync in TimeRelay class. Nothing worked. Could someone point out my mistake in this.
For Clients, it will be null if there is no client connecting to server. For starting Asp.Net Core SignalR and Console App at the same time, the Clients may be null since Index may be called before Console App connects the signalR server.
Try steps below:
Change TimeHub
public class TimeHub: Hub
{
public async Task UpdateTime(string message)
{
if (Clients != null)
{
await Clients.All.SendAsync("ReceiveMessage", message);
}
}
}
Register TimeHub
services.AddSingleton<TimeHub>();
Controller
public class HomeController : Controller
{
private readonly TimeHub _timeHub;
public HomeController(TimeHub timeHub)
{
_timeHub = timeHub;
}
public IActionResult Index()
{
Task.Factory.StartNew(async () =>
{
while (true)
{
try
{
await _timeHub.UpdateTime(DateTime.Now.ToShortDateString());
Thread.Sleep(2000);
}
catch (Exception ex)
{
}
}
});
return View();
}
I got it to work and thought I will answer it here. Thanks #TaoZhou for the tip.
My mistake was sending "UpdateTime" from server and waiting on "ReceiveMessage" at the client.
Ideally the code should look like the following:
SignalR Server:
await context.Clients.All.SendAsync("UpdateTime", DateTime.Now.ToShortDateString());
SignalR Client:
var on = hubConnection.On("UpdateTime", OnReceivedAction);
In this case any message send from the server would be received at the client instantly.
Please refer the code provided in the question for more info.
I've configured hangfire server on my local machine. I use dashboard and added dashboard authorization as it was described in this documentation https://github.com/HangfireIO/Hangfire.Dashboard.Authorization
public void Configuration(IAppBuilder app)
{
GlobalConfiguration.Configuration.UseSqlServerStorage("HangfireDB");
app.UseHangfireServer();
var options = new DashboardOptions()
{
Authorization = new IDashboardAuthorizationFilter[]
{
new DashboardAuthorization()
}
};
app.UseHangfireDashboard("/hangfire", options);
InitializeJobs();
}
public class DashboardAuthorization : IDashboardAuthorizationFilter
{
public bool Authorize([NotNull] DashboardContext context)
{
string ipAddress = HttpContext.Current.Request.UserHostAddress;
return CommonUtils.IsIpAddressValid(ipAddress.Trim());
}
}
The problem is that the Authorize function is called infinitely. Any solution?
I am learning SignalR and it pretty simple. However, I am having a problem sending Messages from Server to client.
My Hub class is as follows:
public class UpdateHub : Hub
{
private readonly UpdateBroadcast _broadcaster;
public UpdateHub() : this(UpdateBroadcast.Instance) { }
public UpdateHub(UpdateBroadcast broadCaster)
{
_broadcaster = broadCaster;
}
}
And I am calling this hub in my broadcaster class like this:
public class UpdateBroadcast
{
private readonly static Lazy<UpdateBroadcast> _instance = new Lazy<UpdateBroadcast>(
() => new UpdateBroadcast(GlobalHost.ConnectionManager.GetHubContext<UpdateHub>()));
private IHubContext _context;
private UpdateBroadcast(IHubContext context)
{
_context = context;
}
public static UpdateBroadcast Instance
{
get { return _instance.Value; }
}
public void UpdatePost(Post post)
{
_context.Clients.All.updatePost(post);
}
}
In my MVC Controller I am calling the UpdatePost method:
public JsonResult AddPost(Post post)
{
UpdateBroadcast broadcaster = UpdateBroadcast.Instance;
Post result = dbFunctions.AddPost(post);
broadcaster.UpdatePost(post);
return Json(new { success = result != null }, JsonRequestBehavior.DenyGet);
}
When I debug the code, I can see that UpdatePost is executed but there is no activity on the client side. My client-side function is like this:
$(function () {
var update = $.connection.updateHub;
update.client.updatePost = function (data) {
alert("called update post");
};
});
I don't seem to understand what is causing this.
Please check below 2 links. I got really helpful with successfully implementation of signalR. Hopefully, this links help you.
https://github.com/vgheri/ChatR
http://www.codeproject.com/Articles/524066/SignalR-Simple-Chat-Application-in-Csharp
I'm trying to set up a signalr hub on my project, using simpleinjector for dependency injection.
I have some configuration in Startup.cs
[assembly: OwinStartup(typeof(Startup))]
namespace CallCentre.Client
{
public class Startup
{
public void Configuration(IAppBuilder app)
{
var container = new Container();
container.Register<TwilioHub>();
container.Verify();
var config = new HubConfiguration()
{
Resolver = new SignalRSimpleInjectorDependencyResolver(container)
};
app.MapSignalR(config);
}
}
}
DI Resolver
public class SignalRSimpleInjectorDependencyResolver : DefaultDependencyResolver
{
private readonly Container _container;
public SignalRSimpleInjectorDependencyResolver(Container container)
{
_container = container;
}
public override object GetService(Type serviceType)
{
return ((IServiceProvider)_container).GetService(serviceType)
?? base.GetService(serviceType);
}
public override IEnumerable<object> GetServices(Type serviceType)
{
return _container.GetAllInstances(serviceType)
.Concat(base.GetServices(serviceType));
}
}
My hub
public class TwilioHub : Hub
{
public void Send(int callCount, int queueId)
{
var context = GlobalHost.ConnectionManager.GetHubContext<TwilioHub>();
context.Clients.All.updateQueueCount(callCount, queueId);
}
}
A class elsewhere in the solution where the hub is called
public class QueueStateHandler : IQueueStateHandler
{
private readonly TwilioHub _twilioHub;
public QueueStateHandler(TwilioHub twilioHub)
{
_twilioHub = twilioHub;
}
public void IncrementQueueById(int id)
{
_twilioHub.Send(5,1);
}
}
And my frontend code
$(function () {
var hub = $.connection.twilioHub;
hub.logging = true;
var queue = $('#QueueCount');
hub.client.updateQueueCount = function(queueCount, id) {
alert(queueCount);
};
$.connection.hub.start();
});
I can set everything up using straight web api, skipping the DI and everything works fine. As soon as I bring in DI I start running into problems. At the moment its silently falling over somewhere. I can step through the code and end up in the hub, but nothing happens in my frontend.
Any pointers as to what I'm doing wrong would be greatly appreciated.
I've an application using SignalR & WebAPI. I've a custom Authorization context based on a token, which I give on each SignalR requests using the QueryString.
I've implement and IUserIdProvider, in order to retrieve my User from the Token.
And finally, I want to call a client method from the server, for a specific User (with is ID), I'm using a HubContext from the GlobalHost.ConnectionManager.
My problem is that my User is never find from my HubContext, but it is from the Hub itself...
Here is my IUserIdProvider implementation
public class SignalRUserIdProvider : IUserIdProvider
{
public string GetUserId(IRequest request)
{
var token = request.QueryString["token"];
var scope = GetUnprotectedScope(token);
if (scope == null)
return null;
return scope.Id_User.ToString();
}
}
Here is my Hub imlementation
[HubName("notifier")]
public class NotifierHub : Hub
{
public void Notify(string message)
{
Clients.User("1").Notify(message); //When call from a client, this works very well, only the User with the Id = 1 receive the notification
}
}
And finally, I use this to call the client method from my server:
GlobalHost
.ConnectionManager
.GetHubContext<NotifierHub>()
.Clients
.User("1")
.Notify(notification.Message);
// This does nothing...
I'm out of solution at this point, I don't understand what happens, does anyone have already achieve this ?
I've finally figured the issue, but I don't know how to fix it...
The actual issue is simple, the Hub itself has a good context with the clients and everything, but the GlobalHost.ConnectionManager have nothing.
If I change my Hub to something like this:
[HubName("notifier")]
public class NotifierHub : Hub
{
public void Notify(string message)
{
Clients.User("1").Notify(message + " from Hub itself");
GlobalHost
.ConnectionManager
.GetHubContext<NotifierHub>()
.Clients
.User("1")
.Notify(message + " from ConnectionManager");
}
}
My client receive "My message from Hub itself", but never receive "My message from ConnectionManager".
In conclusion, I've a problem with my DependencyInjection... I'm using Structuremap, with this DependencyResover :
public class StructureMapSignalRDependencyResolver : DefaultDependencyResolver
{
private IContainer _container;
public StructureMapSignalRDependencyResolver(IContainer container)
{
_container = container;
}
public override object GetService(Type serviceType)
{
if (serviceType == null)
return null;
var service = _container.TryGetInstance(serviceType) ?? base.GetService(serviceType);
if (service != null) return service;
return (!serviceType.IsAbstract && !serviceType.IsInterface && serviceType.IsClass)
? _container.GetInstance(serviceType)
: _container.TryGetInstance(serviceType);
}
public override IEnumerable<object> GetServices(Type serviceType)
{
var objects = _container.GetAllInstances(serviceType).Cast<object>();
return objects.Concat(base.GetServices(serviceType));
}
}
My Startup file:
public void Configuration(IAppBuilder app)
{
app.Map("/signalr", RegisterSignalR);
}
public static void RegisterSignalR(IAppBuilder map)
{
var resolver = new StructureMapSignalRDependencyResolver(IoC.Initialize());
var config = new HubConfiguration { Resolver = resolver };
map.UseCors(CorsOptions.AllowAll);
map.RunSignalR(config);
}
And finally my Registry...
For<Microsoft.AspNet.SignalR.IDependencyResolver>().Add<StructureMapSignalRDependencyResolver>();
For<INotifier>().Use<SignalRNotifier>();