使用SignalR作为EventBus事件的广播员

时间:2015-07-08 13:59:59

标签: javascript signalr durandal event-bus asp.net-boilerplate

我最近使用AspBoilerplate(Abp)启动了一个新项目,并使用SignalR作为某种广播机制来告诉连接的客户端数据库中的某些记录是否已更改或添加或删除。 如果我使用SignalR Hub作为我的AppService的代理,一切正常,并通知客户端

public class TestHub : Hub
{
    IMyAppService = _service
    public TestHub(IMyAppService service)
    {
        _service = service;
    }

    public void CreateEntry(EntryDto entry)
    {
        _service.Create(entry);
        Clients.All.entryCreated(entry);
    }
}

但如果我尝试利用Abp的EventBus的优势,那么我实现了我的AppSevice将事件发送到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;
    }
}

然后我尝试将集线器直接用作EventHandler,但这失败了,因为abp在需要处理事件时创建自己的EventHandler类实例。但这里的代码只是为了完整性:

public  class TestHub : Hub,
    IEventHandler<EntryCreatedEventData>
{ 
      public void Handle(EntryCreatedEventData data)
      {
           Clients.All.entryCreated(data.Entry);
      }
}

在此之后我创建了一个单独的Listener类并尝试使用这样的hub上下文并使用一个非常空的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);
      }
}

在最后一个解决方案中,一切都运行到

_hubContext.Clients.All.entryCreated(eventData.Entry);

但是在我的javascript实现的客户端,永远不会调用该方法。客户端(基于DurandalJs)在使用Hub作为代理和我想要的新方式之间没有变化。

使用信号器

的客户端插件
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
        };
});

使用插件查看:

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 
            } 
       }       
});       

所以任何人都知道为什么当我打电话

时,onCreated从未被调用过
_hubContext.Clients.All.entryCreated(eventData.Entry);

为了测试signalR通信是否正常工作,我添加了一个直接调用客户端方法的方法。调用此方法会将更新成功推送到客户端。所以我认为问题是使用IHubContext远程调用所有客户端的任何线索在使用IHubContext时会出现什么问题?

public class TestHub : Hub
{
    public TestHub ()
        :base()
    { }

    public void Test()
    {
        this.Clients.All.entryCreated(new EntryDto());
    }
}

2 个答案:

答案 0 :(得分:3)

首先,您是否已向DI注册了EntryChangeEventHandler?如果没有,请为EntryChangeEventHandler实现ITransientDependency接口。

您的问题可能与序列化有关。它可能不会序列化eventData.Entry。您可以尝试发送另一个DTO对象。

此外,您可以实施

IEventHandler<EntityChangedEventData<Project>>

以便监听Project实体中的所有更改(包括插入,更新和删除)。 Project只是这里的一个示例实体。

对于您的第一个案例,如果未向DI注册,TestHub将无法运行。您也可以为TestHub类实现ITransientDependency。你应该让SignalR从DI容器中获取它。您可以使用这样的类:

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);
    }
}

然后在启动时设置它:

GlobalHost.DependencyResolver = new WindsorDependencyResolver();

也许我的回答有点令人困惑:)我希望你能理解它。

答案 1 :(得分:1)

经过长时间的搜索,我终于找到了解决办法。

如果您在HubConfiguration中使用自定义依赖项Resolver,就像我一样。例如,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);
    }
}

你不能再使用

_hubContext = GlobalHost.ConnectionManager.GetHubContext<TestHub>();

除非您还将GlobalHost.DependencyResolver设置为WindsorDependencyResolver实例或手动解析对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();

IDependencyResolver resolver = new AutofacDependencyResolver(container);
IHubContext hubContext = resolver.Resolve<IConnectionManager>().GetHubContext<MyHub>();

app.MapSignalR(new HubConfiguration
{
    Resolver = resolver
});