I recently started a new project with the AspBoilerplate (Abp) and to use SignalR as some kind of broadcasting mechanism to tell the connected clients if some records in the databse changed or were added or removed. If i use the SignalR Hub as a proxy to my AppService everything works ok and the client is notified
public class TestHub : Hub
{
IMyAppService = _service
public TestHub(IMyAppService service)
{
_service = service;
}
public void CreateEntry(EntryDto entry)
{
_service.Create(entry);
Clients.All.entryCreated(entry);
}
}
But if i try to leverage the advantages of the EventBus of Abp so i implemented my AppSevice to send Events to the EventBus:
class MyAppService : ApplicationService, IMyAppService
{
public IEventBus EventBus { get; set; }
private readonly IMyRepository _myRepository;
public LicenseAppService(ILicenseRepository myRepository)
{
EventBus = NullEventBus.Instance;
_myRepository = myRepository;
}
public virtual EntryDto CreateLicense(EntryDto input)
{
var newEntry = Mapper.Map<EntryDto >(_myRepository.Insert(input));
EventBus.Trigger(new EntryCreatedEventData { Entry = newEntry});
return newEntry;
}
}
Then i tried to use the hub directly as EventHandler, but this failed because abp creates its own instance of the EventHandler classes whenever it needs to handle an event. But here the code just for completeness:
public class TestHub : Hub,
IEventHandler<EntryCreatedEventData>
{
public void Handle(EntryCreatedEventData data)
{
Clients.All.entryCreated(data.Entry);
}
}
After this i created a seperate Listener class and tried to use the hub context like this and use an pretty empty Hub:
public class TestHub : Hub
{
}
public class EntryChangeEventHandler : IEventHandler<EntryCreatedEventData>
{
private IHubContext _hubContext;
public EntryChangeEventHandler()
{
_hubContext = GlobalHost.ConnectionManager.GetHubContext<TestHub>();
public void Handle(EntryCreatedEventData data)
{
_hubContext.Clients.All.entryCreated(eventData.Entry);
}
}
In the last solution everything runs up to the line
_hubContext.Clients.All.entryCreated(eventData.Entry);
but on the client side in my javascript implementation the method is never called. The client side (based on DurandalJs) didn't change between using the Hub as proxy and the new way i want to go.
Client side plugin for working with signalr
define(["jquery", "signalr.hubs"],
function ($) {
var myHubProxy
function connect(onStarted, onCreated, onEdited, onDeleted) {
var connection = $.hubConnection();
myHubProxy = connection.createHubProxy('TestHub');
connection.connectionSlow(function () {
console.log('We are currently experiencing difficulties with the connection.')
});
connection.stateChanged(function (data) {
console.log('connectionStateChanged from ' + data.oldState + ' to ' + data.newState);
});
connection.error(function (error) {
console.log('SignalR error: ' + error)
});
myHubProxy .on('entryCreated', onCreated);
myHubProxy .on('updated', onEdited);
myHubProxy .on('deleted', onDeleted);
connection.logging = true;
//start the connection and bind functions to send messages to the hub
connection.start()
.done(function () { onStarted(); })
.fail(function (error) { console.log('Could not Connect! ' + error); });
}
return signalr =
{
connect: connect
};
});
view using the plugin:
define(['jquery', 'signalr/myHub],
function ($, myHubSR) {
return function () {
var that = this;
var _$view = null;
that.attached = function (view, parent) {
_$view = $(view);
}
that.activate = function () {
myHubSR.connect(that.onStarted, that.onCreated, that.onEdited, that.onDeleted);
}
that.onStarted= function () {
//do something
}
that.onCreated= function (data) {
//do something
}
that.onEdited = function (data) {
//do something
}
that.onDeleted= function (data) {
//do something
}
}
});
So anyone got a clue why onCreated is never called when i call
_hubContext.Clients.All.entryCreated(eventData.Entry);
?
For testing if the signalR communication works at all i added a method that directly calls a client method. Calling this method the update is pushed to the client successfully. so i think the problem is wiht the remote call to all clients using the IHubContext any clues what could go wrong in the usage of IHubContext?
public class TestHub : Hub
{
public TestHub ()
:base()
{ }
public void Test()
{
this.Clients.All.entryCreated(new EntryDto());
}
}
After long searching in several directions i finally found a solution.
If you use a custom dependency Resolver in the HubConfiguration like i did. For example the implementation from hikalkan:
public class WindsorDependencyResolver : DefaultDependencyResolver
{
public override object GetService(Type serviceType)
{
return IocManager.Instance.IocContainer.Kernel.HasComponent(serviceType) ? IocManager.Instance.IocContainer.Resolve(serviceType) : base.GetService(serviceType);
}
public override IEnumerable<object> GetServices(Type serviceType)
{
return IocManager.Instance.IocContainer.Kernel.HasComponent(serviceType) ? IocManager.Instance.IocContainer.ResolveAll(serviceType).Cast<object>() : base.GetServices(serviceType);
}
}
you can no longer use
_hubContext = GlobalHost.ConnectionManager.GetHubContext<TestHub>();
unless you also set your GlobalHost.DependencyResolver to a instance of WindsorDependencyResolver or manually resolve a reference to IConnectionManager.
GlobalHost.DependencyResolver = new AutofacDependencyResolver(container);
IHubContext hubContext = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
// A custom HubConfiguration is now unnecessary, since MapSignalR will
// use the resolver from GlobalHost by default.
app.MapSignalR();
or
IDependencyResolver resolver = new AutofacDependencyResolver(container);
IHubContext hubContext = resolver.Resolve<IConnectionManager>().GetHubContext<MyHub>();
app.MapSignalR(new HubConfiguration
{
Resolver = resolver
});