91 lines
4.6 KiB
C#
91 lines
4.6 KiB
C#
using Microsoft.AspNetCore.SignalR.Client;
|
|
using Microsoft.AspNetCore.SignalR.Protocol;
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
using Microsoft.Extensions.Logging;
|
|
using Microsoft.Extensions.Options;
|
|
|
|
namespace AyCode.Services.SignalRs;
|
|
|
|
/// <summary>
|
|
/// Client-side registration extension for the <see cref="AyCodeBinaryHubProtocol"/> (<c>"acbinary"</c>).
|
|
/// Mirrors the ASP.NET Core idiomatic pattern of <c>AddJsonProtocol(...)</c> /
|
|
/// <c>AddMessagePackProtocol(...)</c>.
|
|
/// <para>
|
|
/// For the server-side equivalent see <c>AcSignalRServerProtocolExtensions</c> in
|
|
/// <c>AyCode.Services.Server</c> — kept separate to avoid dragging the server SignalR assembly
|
|
/// (<c>Microsoft.AspNetCore.SignalR.Core</c>) into pure client projects (MAUI, WASM).
|
|
/// </para>
|
|
/// </summary>
|
|
public static class AcSignalRProtocolExtensions
|
|
{
|
|
/// <summary>
|
|
/// Registers <see cref="AyCodeBinaryHubProtocol"/> as the protocol for a client <see cref="HubConnection"/>.
|
|
/// Resolves <see cref="AcBinaryHubProtocolOptions"/> from <see cref="IOptions{TOptions}"/> in the
|
|
/// <see cref="HubConnectionBuilder"/>'s inner DI scope.
|
|
/// <para>
|
|
/// ⚠️ <b>Limitation:</b> the outer ASP.NET Core / MAUI / WASM <c>services.Configure<AcBinaryHubProtocolOptions>(...)</c>
|
|
/// registrations do <i>not</i> flow into the <c>HubConnectionBuilder.Services</c> inner collection —
|
|
/// it is an isolated container. This overload therefore always falls back to default options
|
|
/// when used from a client host that uses <c>appsettings.json</c>-driven configuration.
|
|
/// Prefer <see cref="AddAcBinaryProtocol(IHubConnectionBuilder, AcBinaryHubProtocolOptions, Action{AcBinaryHubProtocolOptions})"/>
|
|
/// in that case, passing a pre-resolved <see cref="AcBinaryHubProtocolOptions"/> from the outer service provider.
|
|
/// </para>
|
|
/// </summary>
|
|
public static IHubConnectionBuilder AddAcBinaryProtocol(this IHubConnectionBuilder builder, Action<AcBinaryHubProtocolOptions>? configure = null)
|
|
{
|
|
builder.Services.AddSingleton<IHubProtocol>(sp => BuildProtocol(sp, configure));
|
|
return builder;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Registers <see cref="AyCodeBinaryHubProtocol"/> for a client <see cref="HubConnection"/> using
|
|
/// an <see cref="AcBinaryHubProtocolOptions"/> instance supplied by the caller — typically resolved
|
|
/// from the outer host's <see cref="IOptions{TOptions}"/> and cloned. An optional
|
|
/// <paramref name="configure"/> action may apply last-minute overrides (e.g. attaching a logger
|
|
/// instance) on the cloned options.
|
|
/// <para>
|
|
/// Use this overload when <c>AcBinaryHubProtocolOptions</c> is bound from <c>appsettings.json</c>
|
|
/// via <c>services.Configure<AcBinaryHubProtocolOptions>(...)</c> in the outer host's
|
|
/// service collection — the <see cref="HubConnectionBuilder"/>'s isolated inner DI cannot see
|
|
/// those registrations, so the options must be passed explicitly.
|
|
/// </para>
|
|
/// </summary>
|
|
public static IHubConnectionBuilder AddAcBinaryProtocol(this IHubConnectionBuilder builder, AcBinaryHubProtocolOptions options, Action<AcBinaryHubProtocolOptions>? configure = null)
|
|
{
|
|
if (options is null) throw new ArgumentNullException(nameof(options));
|
|
|
|
builder.Services.AddSingleton<IHubProtocol>(sp =>
|
|
{
|
|
// Clone so callers can safely reuse their options instance across multiple connections
|
|
// without the factory's Logger fallback or configure overrides leaking back.
|
|
var opts = options.Clone();
|
|
opts.Logger ??= sp.GetService<ILogger<AcBinaryHubProtocol>>();
|
|
configure?.Invoke(opts);
|
|
opts.Validate();
|
|
return new AyCodeBinaryHubProtocol(opts);
|
|
});
|
|
|
|
return builder;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Shared factory used by both client (this file) and server
|
|
/// (<c>AcSignalRServerProtocolExtensions</c> in AyCode.Services.Server).
|
|
/// Resolves options from DI (<c>IOptions<T></c>), clones them, applies the inline
|
|
/// <paramref name="configure"/> override, validates, and constructs the protocol.
|
|
/// </summary>
|
|
public static IHubProtocol BuildProtocol(IServiceProvider sp, Action<AcBinaryHubProtocolOptions>? configure)
|
|
{
|
|
var diOptions = sp.GetService<IOptions<AcBinaryHubProtocolOptions>>()?.Value;
|
|
var options = diOptions?.Clone() ?? new AcBinaryHubProtocolOptions();
|
|
|
|
options.Logger ??= sp.GetService<ILogger<AcBinaryHubProtocol>>();
|
|
|
|
configure?.Invoke(options);
|
|
|
|
options.Validate();
|
|
|
|
return new AyCodeBinaryHubProtocol(options);
|
|
}
|
|
}
|