Event aggregation is really a pattern i like, there is something elegant about firing events and subscribers can listen to them without knowing who fired them. This creates decoupled domains both in backends and frontends. Back in 2013, at my customer of that time we saw an increasing demand for pub / sub and immediate update. We fired events on the backend event bus and in the frontend we had SignalR Hubs that picked up the events and forwarded them to the clients. This caused duplicated and similar code both on the client and on the web server. I decided to create an abstraction layer between SignalR and the event aggregator. The result is a library called SignalR.EventAggregatorProxy
It has now been ported to support ASP.NET Core, this article will focus on the changes, for the orginal article go here.
Bootstrap
ASP.NET Core have the new Startup.cs that unifies all your bootstrapping needs. First from ConfigureServices
public void ConfigureServices(IServiceCollection services) { services .AddSignalREventAggregator() .AddSignalR() }
This will hook up all of the libraries different dependencies. Second;
public void Configure(IApplicationBuilder app, IHostingEnvironment env) { app .UseEventProxy() .UseSignalREventAggregator(); }
This will hook up the event proxy script using a custom middleware and also the SignalR hub. Notice that we have removed the generic argument. This was earlier used to identify which event types to proxy out to the clients. This is deprecated, now you instead implement IEventTypeFinder which will supply the library with all the events. We now use the built in Core DI for all custom dependencies, hook up your finder using this.
services .AddSignalREventAggregator() .AddSingleton<IEventTypeFinder, EventTypeFinder>();
Proxy
The event proxy that proxies your events into my library haven’t seen much change, but its now async.
public class EventAggregatorProxy : IEventAggregator, public class EventAggregatorProxy : IEventAggregator, IHandle<Contracts.Events.Event> { private Action<object> handler; public EventAggregatorProxy(Caliburn.Micro.IEventAggregator eventAggregator) { eventAggregator.Subscribe(this); } public void Subscribe(Func<object, Task> handler) { this.handler = handler; } public async Task Handle(Contracts.Events.Event message) { if (handler != null) //Events can come in before the subscriber is hooked up await handler(message); } }
Hook up your proxy using the Core DI.
services .AddSignalREventAggregator() .AddSingleton<IEventAggregator, EventAggregatorProxy>()
Client side library
As said earlier, make sure that you have registered the event proxy script
app.UseEventProxy()
Also include it client side
<script src="/eventAggregation/events" asp-append-version="true"></script>
The client side library is installed using nupgk, right click web project and select Add client side library. Select nupkg from the dropdown and in the searchbox write signalr.eventaggregatorproxy@2 and you should see all versions. Select the latest one. Chose files and choose the client libraries files. Select location and press install.
.NET Client
The library that has seen most change is the .NET Client. First a completely new bootstrap mechanism. We use the core service collection.
serviceProvider = new ServiceCollection() .AddSignalREventAggregator() .WithHubUrl("http://localhost:60976/EventAggregatorProxyHub") .OnConnectionError(e => Debug.WriteLine(e.Message)) .Build()
It also has some callbacks etc you can listen to.
serviceProvider = new ServiceCollection() .AddSignalREventAggregator() .WithHubUrl("http://localhost:60976/EventAggregatorProxyHub") .OnConnectionError(...) .OnSubscriptionError(...) .OnConnected(...) .Build()
Just like the backend the client no longer has a generic argument to specify its events, instead implement the client side version of IEventTypeFinder and register it with the DI. Both your backend and client should share the same Assembly containing the events. This way you ensure that the fully qualified name is same in both cases.
Other than that its mostly the same. I did change the constraint hookup a bit to better support proper constructor injection. You now build the constraints like.
public MyViewModel(IProxyEventAggregator eventAggregator) { eventAggregator.Subscribe(this, builder => builder.Add<MyEvent, MyEventConstraint>(new MyEventConstraint{ Id = 100 })); }
This overload is only available on IProxyEventAggregator former IEventAggregator<TBaseEvent>
Install using nuget
Server package
Install-Package SignalR.EventAggregatorProxyCore
Constraint part only (Good if you dont want dependency to SignalR in Backend etc)
Install-Package SignalR.EventAggregatorProxy.ConstraintCore
.NET Client
Install-Package SignalR.EventAggregatorProxy.Client.DotNetCore
Demo
You can find a Core demo here
Project site here