Oqtane Server Events
By: Shaun Walker
Oqtane 3.1 introduced the INotifyPropertyChanged concept for raising events within your client-side razor components and consuming those events from other components. This solution works very well in a client application for creating rich, responsive and dynamic user interface. But what about server-side events?
Oqtane has always contained a SyncManager on the server which has traditionally only been used to synchronize specific server-side events with client-side state. To describe this in simple terms, in a multi-user environment it is possible that one user could make modifications to a site which would require the other users to refresh the information that is stored in their client-side cache. So on each user interaction the router determines if any sync events have been raised on the server, and if so, it retrieves the new information.
It was always envisioned that this same concept could be used to allow developers to consume other server-side events raised by the core framework. In Oqtane 3.2.1 this capability has now been included using the standard .NET EventHandler capability in conjunction with the SyncManager to raise SyncEvents (which inherit from the standard .NET EventArgs).
The tricky part with event handling in a server-side web context is that most services are short-lived (ie. either scoped or transient) - which is a problem because in order for you to be able to capture an event, you need to have a service which is guaranteed to be running at the precise moment when the event is fired. This is where IHostedServices can be very helpful. IHostedServices startup at the same time as your web application and run on a background thread for the lifetime of your application. This makes them a perfect candidate for capturing events. The challenge with IHostedServices are that they do not support standard dependency injection for scoped services so you need to do some extra work to instantiate those services.
The following diagram describes the various aspects of the server-side event system:
Below is a functional example of how to capture the File Download event. You will notice that it registers the EntityChanged event handler and then inspects the SyncEvent to determine if the Entity and Action match the specific event you are interested in. You should also note that due to the multi-tenant nature of Oqtane it is important to set the tenant before calling any repository methods.
public class FileDownloadHostedService : IHostedService
private readonly IServiceScopeFactory _serviceScopeFactory;
private readonly ISyncManager _syncManager;
public FileDownloadHostedService(IServiceScopeFactory serviceScopeFactory, ISyncManager syncManager)
_serviceScopeFactory = serviceScopeFactory;
_syncManager = syncManager;
void EntityChanged(object sender, SyncEvent e)
// consume SyncEvent
if (e.EntityName == "File" && e.Action == "Download")
using (var scope = _serviceScopeFactory.CreateScope())
// create scoped services
var tenantManager = scope.ServiceProvider.GetRequiredService
var fileRepository = scope.ServiceProvider.GetRequiredService
// set tenant
// get file and do whatever you need
var file = fileRepository.GetFile(e.EntityId);
public Task StartAsync(CancellationToken cancellationToken)
_syncManager.EntityChanged += EntityChanged;
public Task StopAsync(CancellationToken cancellationToken)
public void Dispose()
_syncManager.EntityChanged -= EntityChanged;
All of the core framework entities have been enhanced to raise Add, Update, and Delete events in this manner - which opens up many new opportunities for developers to extend the framework server-side.
With the addition of server-side events, Oqtane now offers support for each of the communication methods required in a typical client/server application.
1. INotifyPropertyChanged events allow for communication between Razor UI components
2. SyncEvents allow for communication between the server and client to signal a UI refresh/reload
3. EntityChanged events allow for communication between server workloads