From ce5ffaae44625565506aad248c67de797e1474b4 Mon Sep 17 00:00:00 2001 From: TekH Date: Thu, 21 May 2026 09:17:39 +0200 Subject: [PATCH] Refactor ReCClient static provider functionality Moved static provider logic to a new partial class `ReCClient.Static.cs` to support legacy scenarios (e.g., .NET Framework) without requiring an external `IServiceProvider`. Introduced new static methods for building and resolving a static `IServiceProvider`: - `BuildStaticClient(Action)` - Overloads for simpler configuration with `apiUri` or `HttpClient`. Marked static methods as `[Obsolete]` to discourage use in modern DI-based applications. Refactored `ReCClient` to focus solely on instance-level functionality, improving code organization and maintainability. Added documentation to clarify the intended use of static methods. --- src/ReC.Client/ReCClient.Static.cs | 142 +++++++++++++++++++++++++++++ src/ReC.Client/ReCClient.cs | 130 +------------------------- 2 files changed, 143 insertions(+), 129 deletions(-) create mode 100644 src/ReC.Client/ReCClient.Static.cs diff --git a/src/ReC.Client/ReCClient.Static.cs b/src/ReC.Client/ReCClient.Static.cs new file mode 100644 index 0000000..ea134d2 --- /dev/null +++ b/src/ReC.Client/ReCClient.Static.cs @@ -0,0 +1,142 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using System; +using System.Net.Http; + +namespace ReC.Client +{ + /// + /// Static convenience entry-point for building and resolving a without an + /// externally provided . Intended for legacy scenarios (e.g. .NET Framework + /// codebases without an established DI container). For new code, prefer + /// . + /// + public partial class ReCClient + { +#if NET8_0_OR_GREATER + private static Action? _staticConfigure = null; +#else + private static Action _staticConfigure = null; +#endif + + private static readonly Lazy LazyProvider = new Lazy(() => + { + var configure = _staticConfigure + ?? throw new InvalidOperationException("Static Provider is not built. Call BuildStaticClient first."); + + var services = new ServiceCollection(); + configure(services); + return services.BuildServiceProvider(); + }, System.Threading.LazyThreadSafetyMode.ExecutionAndPublication); + + /// + /// Configures and builds the static for creating instances. + /// + /// + /// This method should only be called once during application startup. The underlying + /// is created lazily and thread-safely on first access via . + /// + /// Callback that populates a instance. + /// Thrown when is null. + /// Thrown when neither nor is set, when both are set, or when the static provider has already been built. + [Obsolete("Use a local service collection instead of the static provider.")] + public static void BuildStaticClient(Action configure) + { + if (configure == null) + throw new ArgumentNullException(nameof(configure)); + + var cfg = new StaticBuildConfiguration(); + configure(cfg); + + var hasBaseAddress = !string.IsNullOrWhiteSpace(cfg.BaseAddress); + var hasConfigureClient = cfg.ConfigureClient != null; + + if (!hasBaseAddress && !hasConfigureClient) + throw new InvalidOperationException( + $"Either {nameof(StaticBuildConfiguration.BaseAddress)} or {nameof(StaticBuildConfiguration.ConfigureClient)} must be set on {nameof(StaticBuildConfiguration)}."); + + if (hasBaseAddress && hasConfigureClient) + throw new InvalidOperationException( + $"{nameof(StaticBuildConfiguration.BaseAddress)} and {nameof(StaticBuildConfiguration.ConfigureClient)} are mutually exclusive on {nameof(StaticBuildConfiguration)}."); + + Action register = services => + { + if (hasBaseAddress) + services.AddRecClient(cfg.BaseAddress, cfg.ConfigureOptions); + else + services.AddRecClient(cfg.ConfigureClient, cfg.ConfigureOptions); + + if (cfg.Logger != null) + services.AddSingleton(cfg.Logger); + + cfg.ConfigureServices?.Invoke(services); + }; + + if (System.Threading.Interlocked.CompareExchange(ref _staticConfigure, register, null) != null) + throw new InvalidOperationException("Static Provider is already built."); + } + + /// + /// Configures and builds the static for creating instances. + /// + /// + /// This method should only be called once during application startup. + /// The underlying is created lazily and thread-safely on first access via . + /// + /// The base URI of the ReC API. + /// An optional callback to configure . + /// An optional instance to be used by the . When provided, it is registered as a singleton in the internal service collection. + /// Thrown if the static provider has already been built. + [Obsolete("Use BuildStaticClient(Action) instead.")] +#if NETFRAMEWORK + public static void BuildStaticClient(string apiUri, Action configureOptions = null, ILogger logger = null) +#else + public static void BuildStaticClient(string apiUri, Action? configureOptions = null, ILogger? logger = null) +#endif + { + BuildStaticClient(cfg => + { + cfg.BaseAddress = apiUri; + cfg.ConfigureOptions = configureOptions; + cfg.Logger = logger; + }); + } + + /// + /// Configures and builds the static for creating instances. + /// + /// + /// This method should only be called once during application startup. + /// The underlying is created lazily and thread-safely on first access via . + /// + /// An action to configure the . + /// An optional callback to configure . + /// An optional instance to be used by the . When provided, it is registered as a singleton in the internal service collection. + /// Thrown if the static provider has already been built. + [Obsolete("Use BuildStaticClient(Action) instead.")] +#if NETFRAMEWORK + public static void BuildStaticClient(Action configureClient, Action configureOptions = null, ILogger logger = null) +#else + public static void BuildStaticClient(Action configureClient, Action? configureOptions = null, ILogger? logger = null) +#endif + { + BuildStaticClient(cfg => + { + cfg.ConfigureClient = configureClient; + cfg.ConfigureOptions = configureOptions; + cfg.Logger = logger; + }); + } + + /// + /// Creates a new instance using the statically configured provider. + /// + /// A new instance of the . + /// Thrown if has not been called yet. + [Obsolete("Use a local service collection instead of the static provider.")] + public static ReCClient Create() + { + return LazyProvider.Value.GetRequiredService(); + } + } +} diff --git a/src/ReC.Client/ReCClient.cs b/src/ReC.Client/ReCClient.cs index fc537e9..5f54fee 100644 --- a/src/ReC.Client/ReCClient.cs +++ b/src/ReC.Client/ReCClient.cs @@ -10,7 +10,7 @@ namespace ReC.Client /// /// A client for interacting with the ReC API. /// - public class ReCClient + public partial class ReCClient { private readonly HttpClient _http; @@ -83,134 +83,6 @@ namespace ReC.Client Endpoints = new EndpointsApi(_http, logger, opts); Common = new CommonApi(_http, logger, opts); } - - #region Static -#if NET8_0_OR_GREATER - private static Action? _staticConfigure = null; -#else - private static Action _staticConfigure = null; -#endif - - private static readonly Lazy LazyProvider = new Lazy(() => - { - var configure = _staticConfigure - ?? throw new InvalidOperationException("Static Provider is not built. Call BuildStaticClient first."); - - var services = new ServiceCollection(); - configure(services); - return services.BuildServiceProvider(); - }, System.Threading.LazyThreadSafetyMode.ExecutionAndPublication); - - /// - /// Configures and builds the static for creating instances. - /// - /// - /// This method should only be called once during application startup. The underlying - /// is created lazily and thread-safely on first access via . - /// - /// Callback that populates a instance. - /// Thrown when is null. - /// Thrown when neither nor is set, when both are set, or when the static provider has already been built. - [Obsolete("Use a local service collection instead of the static provider.")] - public static void BuildStaticClient(Action configure) - { - if (configure == null) - throw new ArgumentNullException(nameof(configure)); - - var cfg = new StaticBuildConfiguration(); - configure(cfg); - - var hasBaseAddress = !string.IsNullOrWhiteSpace(cfg.BaseAddress); - var hasConfigureClient = cfg.ConfigureClient != null; - - if (!hasBaseAddress && !hasConfigureClient) - throw new InvalidOperationException( - $"Either {nameof(StaticBuildConfiguration.BaseAddress)} or {nameof(StaticBuildConfiguration.ConfigureClient)} must be set on {nameof(StaticBuildConfiguration)}."); - - if (hasBaseAddress && hasConfigureClient) - throw new InvalidOperationException( - $"{nameof(StaticBuildConfiguration.BaseAddress)} and {nameof(StaticBuildConfiguration.ConfigureClient)} are mutually exclusive on {nameof(StaticBuildConfiguration)}."); - - Action register = services => - { - if (hasBaseAddress) - services.AddRecClient(cfg.BaseAddress, cfg.ConfigureOptions); - else - services.AddRecClient(cfg.ConfigureClient, cfg.ConfigureOptions); - - if (cfg.Logger != null) - services.AddSingleton(cfg.Logger); - - cfg.ConfigureServices?.Invoke(services); - }; - - if (System.Threading.Interlocked.CompareExchange(ref _staticConfigure, register, null) != null) - throw new InvalidOperationException("Static Provider is already built."); - } - - /// - /// Configures and builds the static for creating instances. - /// - /// - /// This method should only be called once during application startup. - /// The underlying is created lazily and thread-safely on first access via . - /// - /// The base URI of the ReC API. - /// An optional callback to configure . - /// An optional instance to be used by the . When provided, it is registered as a singleton in the internal service collection. - /// Thrown if the static provider has already been built. - [Obsolete("Use BuildStaticClient(Action) instead.")] -#if NETFRAMEWORK - public static void BuildStaticClient(string apiUri, Action configureOptions = null, ILogger logger = null) -#else - public static void BuildStaticClient(string apiUri, Action? configureOptions = null, ILogger? logger = null) -#endif - { - BuildStaticClient(cfg => - { - cfg.BaseAddress = apiUri; - cfg.ConfigureOptions = configureOptions; - cfg.Logger = logger; - }); - } - - /// - /// Configures and builds the static for creating instances. - /// - /// - /// This method should only be called once during application startup. - /// The underlying is created lazily and thread-safely on first access via . - /// - /// An action to configure the . - /// An optional callback to configure . - /// An optional instance to be used by the . When provided, it is registered as a singleton in the internal service collection. - /// Thrown if the static provider has already been built. - [Obsolete("Use BuildStaticClient(Action) instead.")] -#if NETFRAMEWORK - public static void BuildStaticClient(Action configureClient, Action configureOptions = null, ILogger logger = null) -#else - public static void BuildStaticClient(Action configureClient, Action? configureOptions = null, ILogger? logger = null) -#endif - { - BuildStaticClient(cfg => - { - cfg.ConfigureClient = configureClient; - cfg.ConfigureOptions = configureOptions; - cfg.Logger = logger; - }); - } - - /// - /// Creates a new instance using the statically configured provider. - /// - /// A new instance of the . - /// Thrown if has not been called yet. - [Obsolete("Use a local service collection instead of the static provider.")] - public static ReCClient Create() - { - return LazyProvider.Value.GetRequiredService(); - } - #endregion } ///