RossWright.MetalShout
9.0.0-alpha001
dotnet add package RossWright.MetalShout --version 9.0.0-alpha001
NuGet\Install-Package RossWright.MetalShout -Version 9.0.0-alpha001
<PackageReference Include="RossWright.MetalShout" Version="9.0.0-alpha001" />
paket add RossWright.MetalShout --version 9.0.0-alpha001
#r "nuget: RossWright.MetalShout, 9.0.0-alpha001"
// Install RossWright.MetalShout as a Cake Addin #addin nuget:?package=RossWright.MetalShout&version=9.0.0-alpha001&prerelease // Install RossWright.MetalShout as a Cake Tool #tool nuget:?package=RossWright.MetalShout&version=9.0.0-alpha001&prerelease
Ross Wright's Metal Shout
by Ross Wright
Copyright 2023 Pross Co. All Rights Reserved.
Description
Easily push messages from the server to the client by user.
Pre-requisites
Metal Shout requires Metal Guardian which it uses for managing HTTP connections and authentication. If you have a scenario where you do not need authentication, you're better off implementing your own solution on SignalR. Metal Shout is based on sending objects to a specific user, thus authentication is required to identify the user behind each connected client.
Setup
To setup Metal Shout on the server, add the RossWright.MetalShout.Server package and call: AddMetalShout
on the ServiceCollection in Program.cs and call UseMetalShout()
after the Build call but before the Run call,
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddMetalShout();
...
var app = builder.Build();
...
app.UseMetalShout();
...
app.Run();
Similarly, to setup Metal Shout on the client, add the RossWright.MetalShout package and call: AddMetalShout
on the ServiceCollection in Program.cs. If you are using Switchboard (see below) or want the connection to the server to open on login and close on logout, call UseMetalShout()
on the built app after the Build call but before the Run call,
var builder = WebAssemblyHostBuilder.CreateBuilder(args);
builder.Services.AddMetalShout();
...
var app = builder.Build();
...
app.UseMetalShout(); //if using Switchboard, or want connection linked to authentication
...
app.Run();
Push Messages
A push message can be an instance of any class known to both the client and server that can be serialized (no circular references, etc.). Note there is a fairly high size limit on messages imposed by SignalR which MetalPush is based on, but the recommended pattern is for messages to contain only the information absolutely neccisary (reference IDs, etc) and have the client retrieve any substantial amount of data via an API.
Pushing Messages from the Server
All messages are pushed to a user. If the user has multiple active clients active, the message will be pushed to all of those clients simultaneously. What users receive a push message can be optionally specified by user ID at the time the message is pushed,
pushService.Push(new MyPushObject(), user.Id);
or by subscribing the user to the type of push message in one place (perhaps on login or when a user enters a chat room) and pushing it in another, separating the concerns of tracking who needs to receive what messages and when the messages need to be sent,
pushService.SubscribeUser<MyPushObject>(user.Id);
...
pushService.Push(new MyPushObject());
Users can also be unsubscribed in a similar fashion,
pushService.UnsubscribeUser<MyPushObject>(user.Id);
Reference IDs
When pushing a message or subscribing a user, an optional reference ID can be specified. This allows you to subscribe a user to receive push messages of a specific typeonly if it is pushed with the specified reference id. Any users subscribed for the push message type without a reference ID will also receive messages pushed with a reference ID. This looks like:
var message = new MyPushObject() { Id = "1234" };
pushService.Push(message, message.Id, user.Id);
or
pushService.SubscribeUser<MyPushObject>(user.Id, "1234");
...
var message = new MyPushObject() { Id = "1234" };
pushService.Push(message, message.Id);
and
pushService.UnsubscribeUser<MyPushObject>(user.Id, "1234");
Subscribe for all reference IDs and users
When subscribing a user you can specify the user should receive all push messages of the type regardless of the reference id or the users the message was sent to. This is useful for administrative users or monitoring dashboards. For performance sake, you should limit the lifetime of such subscriptions to just the period of time it is needed (such as only while the monitoring page is open on a client). This looks like:
pushService.SubscribeUser<MyPushObject>(userOne.Id, forAllRefsAndUsers: true);
...
var message = new MyPushObject() { Id = "1234" };
pushService.Push(message, message.Id, userTwo.Id); // userOne also gets the message
Connection Observation
Even though a user might have logged in, their connection to the server to receive push messages is dependent on having an active subscription on the client-side (see below). MetalShout tracks the connection of each user's clients, but you can hook into Metal Shout to take actions when a user connects (and disconnects) to the server for push messages by registering a service as a IPushConnectionObserver
. MetalShout supports multiple observers.
public interface IPushConnectionObserver
{
Task OnConnected(Guid? userId, bool isFirstConnection);
Task OnDisconnected(Guid? userId, Exception? exception, bool wasLastConnection);
}
Endpoint Name
The default SignalR Hub Endpoint for Metal Shout is "/PushHub". You can change this by passing a parameter to the AddPushService
call on the client and the UsePushService
call on the server.
builder.Services.AddMetalShout("MyChatHub");
and
app.UseMetalShout("MyChatHub");
Receiving Push Messages on the Client
To subscribe to receive push messages on the client, there are three ways to use Metal Shout:
Subscribe Using the Push Service
To subscribe to receive push messages using the push service inject 'IPushService
and call Subscribe
, keeping a reference to the IDisposable momento it returns, implementing IAsyncDisposable on your component and then disposing the momento in your component's DisposeAsync method.
[Inject] public IPushService PushService { get; set; } = null!;
private IAsyncDisposable? _subscription;
protected override async Task OnInitializedAsync()
{
_subscription = await PushService.Subscribe<MyPushMessage>(OnMyPushMessage),
}
public async ValueTask DisposeAsync()
{
if (_subscription != null)
{
await _subscription.DisposeAsync();
}
}
private async Task OnMyPushMessage(MyPushMessage msg)
{
// handle the message, calling StateHasChanged() to re-render the component since this is not a UI-generated call
}
This method has the benefit that the connection to the server will only be maintained if there is an active subscription.
Using Switchboard
Switchboard is a service provided as part of Metal Core that allows you to send messages within a process. For example, you might send a message from a profile edit screen and subscribe to it from your page header to detect when the currently logged-in user has changed their profile name or avatar and update the UI. This is similar to MediatR Notifications but uses a subscription model instead of a handler model. When Switchboard has been registered as a service, Metal Shout will also broadcast received push messages via the Switchboard.
To setup Switchboard, call AddSwitchboard
on the services collection and UseMetalShout
on the built app after the Build call but before the Run call in Program.cs,
var builder = WebAssemblyHostBuilder.CreateBuilder(args);
builder.Services.AddMetalShout();
builder.Services.AddSwitchboard();
...
var app = builder.Build();
...
app.UseMetalShout();
...
app.Run();
and to subscribe to receive messages on the Switchboard in a component, inject ISwitchboard
and call Subscribe
keeping a reference to the IDisposable momento it returns, implementing IDisposable on your component and then disposing the momento in your component's Dispose method.
[Inject] public ISwitchboard Switchboard { get; set; } = null!;
private IDisposable? _subscription;
protected override void OnInitialized()
{
_subscription = Switchboard.Subscribe<MyPushMessage>(OnMyPushMessage),
}
public void Dispose()
{
if (_subscription != null)
{
_subscription.Dispose();
}
}
private async Task OnMyPushMessage(MyPushMessage msg)
{
// handle the message, calling StateHasChanged() to re-render the component since this is not a UI-generated call
}
A benefit of subscribing to push messages via the Switchboard is that in addition to receiving pushes from the server you can also send the same message from code in the client.
[Inject] public ISwitchboard Switchboard { get; set; } = null!;
...
private async Task OnClick()
{
await Switchboard.SendAsync(new MyPushMessage());
}
Subscribe using an IPushSubscriber
If you need a reusable subscription handler, you can implement IPushSubscriber
,
public class PushObjSubscriber : IPushSubscriber<MyPushMessage>
{
public Task OnPushReceived(MyPushMessage message)
{
// process the incoming push object here
}
}
and then where appropriate, you can call subscribe with an instance of your Push Subscriber
[Inject] public IPushService PushService { get; set; } = null!;
...
var subscriber = new PushObjSubscriber();
await PushService.Subscribe(subscriber);
and then when you no longer need the push subscriber,
await pushService.Unsubscribe(subscriber);
Using multiple servers
The examples above assume the client is connecting to a server on the default connection as setup by AddMetalGuardianHttpClient
. To connect to a different server, pass the connection name provided to Metal Guardian to IPushService.Subscribe
and IPushService.Unsubscribe
.
For Switchboard, UseMetalShout
also accepts a connectionName parameter, but messages of the same type from any connection are pushed to all subscribers for that type. If your handler needs to know which connection a message has come from either use the IPushService
subscription methods to limit the messages recieve by your handler to one connection or encode that information in your message.
Licensing
A license must be purchased to use RossWright.Metal libaries in a production environment. For development enviroments, using the libraries without a license will show a console message on initialization and cease functioning after one hour. To install your license file include it in the executable project with the Build Action set to Embedded Resource. The file can be renamed as needed, but must end with the extension .license.
Product | Versions Compatible and additional computed target framework versions. |
---|---|
.NET | net9.0 is compatible. |
-
net9.0
- Microsoft.AspNetCore.SignalR.Client (>= 9.0.0)
- RossWright.MetalGuardian (>= 9.0.0-alpha001)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on RossWright.MetalShout:
Package | Downloads |
---|---|
RossWright.MetalGrind
MetalGrind client-side library |
|
RossWright.MetalShout.Server
MetalShout Server-side |
GitHub repositories
This package is not used by any popular GitHub repositories.
Version | Downloads | Last updated |
---|---|---|
9.0.0-alpha001 | 0 | 12/1/2024 |
8.0.0 | 5 | 11/30/2024 |
8.0.0-beta007 | 69 | 9/28/2024 |