3

Недавно я начал новый проект с AspBoilerplate (Abp) и использовал SignalR как своего рода механизм вещания, чтобы сообщить подключенным клиентам, если некоторые записи в базе данных изменились или были добавлены или удалены. Если я использовать SignalR Hub в качестве прокси для моей AppService все работает нормально, и клиент получает уведомлениеИспользовать SignalR как транслятор для событий EventBus

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

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

Но если я пытаюсь использовать преимущества 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, но это не удалось, потому что АБП создает свой собственный экземпляр EventHandler классов всякий раз, когда ему необходимо обработать событие. Но вот код только для полноты:

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

После этого я создал отдельный класс слушателя и пытался использовать контекст ступицы, как это и использовать довольно пустой концентратор:

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 в качестве прокси и новым способом, который я хочу использовать.

стороне клиента плагин для работы с 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 
     }; 
}); 

вид с помощью плагина:

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

Может быть выключен, но где это 'функция entryCreated' определяется на стороне клиента? – rdoubleui

+0

thx, так как вы комментируете, что вы правы в опубликованном коде, возникла ошибка, и я переименовал классы и методы. должен был изменить строку myHubProxy .on ('entryCreated', onCreated); в реальном коде имя метода написано правильно – BoeseB

+0

Является ли OnCreated callback единственным, что не достигнуто? – rdoubleui

ответ

1

После долгих поисков в нескольких направлениях я наконец нашел решение, благодаря this site

При использовании пользовательской зависимости резольвера в 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 
}); 
3

Во-первых, вы зарегистрировались в EntryChangeEventHandler для DI? Если нет, реализуйте также интерфейс ITransientDependency для EntryChangeEventHandler.

Ваша проблема может быть связана с сериализацией. Он не может сериализовать eventData.Entry. Вы можете попробовать отправить другой объект DTO.

Кроме того, вы можете реализовать

IEventHandler<EntityChangedEventData<Project>> 

, чтобы прослушать все изменения в сущности проекта (включая вставки, обновления и удаления). Проект - всего лишь образец объекта.

Для вашего первого случая, TestHub не может работать, если он не зарегистрирован в DI. Вы также можете реализовать ITransientDependency для класса TestHub.И вы должны сделать 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 = новый WindsorDependencyResolver();

Возможно, мой ответ был немного запутанным :) Надеюсь, вы сможете это понять.

+0

Спасибо за ваш быстрый ответ. Успешно создан элемент EntryChangeEventHandler, я также реализовал WindsorDependencyResolver и добавил IConventionalDependencyRegistrar для концентраторов SignalR. Dto успешно сериализуется в других местах и ​​имеет то же самое, что и в первом первом примере, когда концентратор напрямую связывается с сервисом – BoeseB