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