【问题标题】:Use SignalR as Broadcaster for EventBus Events使用 SignalR 作为 EventBus 事件的广播器
【发布时间】:2015-07-08 13:59:59
【问题描述】:

我最近使用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 类并尝试像这样使用集线器上下文并使用一个非常空的集线器:

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

【问题讨论】:

  • 可以关闭,但是客户端定义的entryCreated函数在哪里?
  • 感谢您的评论,您在发布的代码中是正确的,因为我重命名了类和方法,因此出现错误。必须更改行 myHubProxy .on('entryCreated', onCreated);在实际代码中方法名称写得正确
  • OnCreated 回调是唯一未达到的回调吗?
  • 都没有调用,只是到达了onStartedCallback
  • 所以我同意,这一定是 JS 代码中的问题,很可能我认为这是范围问题。 onStarted 是作为 done 委托传递的,其他的附加在 myHubProxy 上。

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


【解决方案1】:

首先,你有向 DI 注册 EntryChangeEventHandler 吗?如果没有,也为 EntryChangeEventHandler 实现 ITransientDependency 接口。

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

另外,你可以实现

IEventHandler<EntityChangedEventData<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();

也许我的回答有点混乱 :) 希望你能理解。

【讨论】:

  • 感谢您的快速回复。 EntryChangeEventHandler 已成功创建,我还实现了 WindsorDependencyResolver 并为 SignalR 集线器添加了 IConventionalDependencyRegistrar。 Dto 在其他地方成功序列化,它与第一个工作示例中的 dto 相同,其中集线器直接与 appservice 通信
【解决方案2】:

找了好几个方向,终于找到了解决办法。

如果您像我一样在 HubConfiguration 中使用自定义依赖解析器。例如来自 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
});

【讨论】:

    猜你喜欢
    • 2013-03-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-10-22
    • 1970-01-01
    • 2017-11-11
    • 1970-01-01
    相关资源
    最近更新 更多