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<StaticBuildConfiguration>)` - 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.
This commit is contained in:
142
src/ReC.Client/ReCClient.Static.cs
Normal file
142
src/ReC.Client/ReCClient.Static.cs
Normal file
@@ -0,0 +1,142 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
|
||||
namespace ReC.Client
|
||||
{
|
||||
/// <summary>
|
||||
/// Static convenience entry-point for building and resolving a <see cref="ReCClient"/> without an
|
||||
/// externally provided <see cref="IServiceProvider"/>. Intended for legacy scenarios (e.g. .NET Framework
|
||||
/// codebases without an established DI container). For new code, prefer
|
||||
/// <see cref="DependencyInjection.AddRecClient(IServiceCollection, string, Action{ReCClientOptions})"/>.
|
||||
/// </summary>
|
||||
public partial class ReCClient
|
||||
{
|
||||
#if NET8_0_OR_GREATER
|
||||
private static Action<IServiceCollection>? _staticConfigure = null;
|
||||
#else
|
||||
private static Action<IServiceCollection> _staticConfigure = null;
|
||||
#endif
|
||||
|
||||
private static readonly Lazy<IServiceProvider> LazyProvider = new Lazy<IServiceProvider>(() =>
|
||||
{
|
||||
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);
|
||||
|
||||
/// <summary>
|
||||
/// Configures and builds the static <see cref="IServiceProvider"/> for creating <see cref="ReCClient"/> instances.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method should only be called once during application startup. The underlying
|
||||
/// <see cref="IServiceProvider"/> is created lazily and thread-safely on first access via <see cref="Create"/>.
|
||||
/// </remarks>
|
||||
/// <param name="configure">Callback that populates a <see cref="StaticBuildConfiguration"/> instance.</param>
|
||||
/// <exception cref="ArgumentNullException">Thrown when <paramref name="configure"/> is null.</exception>
|
||||
/// <exception cref="InvalidOperationException">Thrown when neither <see cref="StaticBuildConfiguration.BaseAddress"/> nor <see cref="StaticBuildConfiguration.ConfigureClient"/> is set, when both are set, or when the static provider has already been built.</exception>
|
||||
[Obsolete("Use a local service collection instead of the static provider.")]
|
||||
public static void BuildStaticClient(Action<StaticBuildConfiguration> 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<IServiceCollection> 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.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures and builds the static <see cref="IServiceProvider"/> for creating <see cref="ReCClient"/> instances.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method should only be called once during application startup.
|
||||
/// The underlying <see cref="IServiceProvider"/> is created lazily and thread-safely on first access via <see cref="Create"/>.
|
||||
/// </remarks>
|
||||
/// <param name="apiUri">The base URI of the ReC API.</param>
|
||||
/// <param name="configureOptions">An optional callback to configure <see cref="ReCClientOptions"/>.</param>
|
||||
/// <param name="logger">An optional <see cref="ILogger"/> instance to be used by the <see cref="ReCClient"/>. When provided, it is registered as a singleton in the internal service collection.</param>
|
||||
/// <exception cref="InvalidOperationException">Thrown if the static provider has already been built.</exception>
|
||||
[Obsolete("Use BuildStaticClient(Action<StaticBuildConfiguration>) instead.")]
|
||||
#if NETFRAMEWORK
|
||||
public static void BuildStaticClient(string apiUri, Action<ReCClientOptions> configureOptions = null, ILogger logger = null)
|
||||
#else
|
||||
public static void BuildStaticClient(string apiUri, Action<ReCClientOptions>? configureOptions = null, ILogger<ReCClient>? logger = null)
|
||||
#endif
|
||||
{
|
||||
BuildStaticClient(cfg =>
|
||||
{
|
||||
cfg.BaseAddress = apiUri;
|
||||
cfg.ConfigureOptions = configureOptions;
|
||||
cfg.Logger = logger;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Configures and builds the static <see cref="IServiceProvider"/> for creating <see cref="ReCClient"/> instances.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This method should only be called once during application startup.
|
||||
/// The underlying <see cref="IServiceProvider"/> is created lazily and thread-safely on first access via <see cref="Create"/>.
|
||||
/// </remarks>
|
||||
/// <param name="configureClient">An action to configure the <see cref="HttpClient"/>.</param>
|
||||
/// <param name="configureOptions">An optional callback to configure <see cref="ReCClientOptions"/>.</param>
|
||||
/// <param name="logger">An optional <see cref="ILogger"/> instance to be used by the <see cref="ReCClient"/>. When provided, it is registered as a singleton in the internal service collection.</param>
|
||||
/// <exception cref="InvalidOperationException">Thrown if the static provider has already been built.</exception>
|
||||
[Obsolete("Use BuildStaticClient(Action<StaticBuildConfiguration>) instead.")]
|
||||
#if NETFRAMEWORK
|
||||
public static void BuildStaticClient(Action<HttpClient> configureClient, Action<ReCClientOptions> configureOptions = null, ILogger logger = null)
|
||||
#else
|
||||
public static void BuildStaticClient(Action<HttpClient> configureClient, Action<ReCClientOptions>? configureOptions = null, ILogger<ReCClient>? logger = null)
|
||||
#endif
|
||||
{
|
||||
BuildStaticClient(cfg =>
|
||||
{
|
||||
cfg.ConfigureClient = configureClient;
|
||||
cfg.ConfigureOptions = configureOptions;
|
||||
cfg.Logger = logger;
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new <see cref="ReCClient"/> instance using the statically configured provider.
|
||||
/// </summary>
|
||||
/// <returns>A new instance of the <see cref="ReCClient"/>.</returns>
|
||||
/// <exception cref="InvalidOperationException">Thrown if <see cref="BuildStaticClient(Action{StaticBuildConfiguration})"/> has not been called yet.</exception>
|
||||
[Obsolete("Use a local service collection instead of the static provider.")]
|
||||
public static ReCClient Create()
|
||||
{
|
||||
return LazyProvider.Value.GetRequiredService<ReCClient>();
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user