diff --git a/Directory.Build.props b/Directory.Build.props
index f1e1da0..6acef26 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -8,9 +8,8 @@
1.0.3
-
- 1.3.3
+
+ 1.3.4
1.0.0
diff --git a/src/Features/PowerCSharp.Feature.Cache.Abstractions/NoOp/NoOpCacheService.cs b/src/Features/PowerCSharp.Feature.Cache.Abstractions/NoOp/NoOpCacheService.cs
index 99b08dc..2ad548f 100644
--- a/src/Features/PowerCSharp.Feature.Cache.Abstractions/NoOp/NoOpCacheService.cs
+++ b/src/Features/PowerCSharp.Feature.Cache.Abstractions/NoOp/NoOpCacheService.cs
@@ -14,7 +14,13 @@ public sealed class NoOpCacheService : ICacheService
/// Creates the NoOp cache and logs that caching is inert.
public NoOpCacheService(ILogger logger)
{
- logger.LogInformation("Cache feature is disabled or unconfigured; using NoOp in-memory cache.");
+ // NOTE: this constructor can run even when a real provider (e.g. BitFaster) is the one
+ // actually injected as ICacheService — ASP.NET Core's ValidateOnBuild (on by default in
+ // Development) constructs every registered descriptor, including this shadowed safe-off
+ // floor, purely to validate the DI graph. Seeing this log does not by itself mean the app
+ // is using NoOp; check the Cache feature's "Cache feature resolved" diagnostic line for
+ // the implementation actually bound to ICacheService.
+ logger.LogInformation("NoOp in-memory cache constructed (ICacheService safe-off floor).");
}
///
diff --git a/src/Features/PowerCSharp.Feature.Cache.Abstractions/NoOp/NoOpDiskCacheService.cs b/src/Features/PowerCSharp.Feature.Cache.Abstractions/NoOp/NoOpDiskCacheService.cs
index 0afbaed..0fd34d1 100644
--- a/src/Features/PowerCSharp.Feature.Cache.Abstractions/NoOp/NoOpDiskCacheService.cs
+++ b/src/Features/PowerCSharp.Feature.Cache.Abstractions/NoOp/NoOpDiskCacheService.cs
@@ -14,7 +14,11 @@ public sealed class NoOpDiskCacheService : IDiskCacheService
/// Creates the NoOp disk cache and logs that disk caching is inert.
public NoOpDiskCacheService(ILogger logger)
{
- logger.LogInformation("Cache feature is disabled or unconfigured; using NoOp disk cache.");
+ // NOTE: see the matching comment in NoOpCacheService — this can be constructed by
+ // ASP.NET Core's ValidateOnBuild even when a real provider (e.g. Disk) is the one
+ // actually injected as IDiskCacheService. Check the Cache feature's "Cache feature
+ // resolved" diagnostic line for the implementation actually in use.
+ logger.LogInformation("NoOp disk cache constructed (IDiskCacheService safe-off floor).");
}
///
diff --git a/src/Features/PowerCSharp.Feature.Cache.BitFaster/BitFasterCacheService.cs b/src/Features/PowerCSharp.Feature.Cache.BitFaster/BitFasterCacheService.cs
index f4c3aa1..04c0d6a 100644
--- a/src/Features/PowerCSharp.Feature.Cache.BitFaster/BitFasterCacheService.cs
+++ b/src/Features/PowerCSharp.Feature.Cache.BitFaster/BitFasterCacheService.cs
@@ -1,6 +1,7 @@
using System.Collections.Concurrent;
using System.Diagnostics;
using BitFaster.Caching.Lru;
+using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using PowerCSharp.Feature.Cache.Abstractions;
@@ -20,10 +21,16 @@ public sealed class BitFasterCacheService : ICacheService
private readonly ConcurrentDictionary _metadata = new();
/// Creates the cache with capacity from .
- public BitFasterCacheService(IOptions options)
+ public BitFasterCacheService(IOptions options, ILogger logger)
{
var capacity = Math.Max(1, options.Value.Capacity);
_cache = new ConcurrentLru(capacity);
+
+ logger.LogInformation(
+ "BitFaster in-memory cache initialized (capacity={Capacity}). This constructor running does not " +
+ "by itself confirm this instance is the one injected as ICacheService — see the Cache feature's " +
+ "'Cache feature resolved' diagnostic line for the actually-bound implementation.",
+ capacity);
}
///
diff --git a/src/Features/PowerCSharp.Feature.Cache.BitFaster/PowerCSharp.Feature.Cache.BitFaster.csproj b/src/Features/PowerCSharp.Feature.Cache.BitFaster/PowerCSharp.Feature.Cache.BitFaster.csproj
index 4fc8ff0..250b2d3 100644
--- a/src/Features/PowerCSharp.Feature.Cache.BitFaster/PowerCSharp.Feature.Cache.BitFaster.csproj
+++ b/src/Features/PowerCSharp.Feature.Cache.BitFaster/PowerCSharp.Feature.Cache.BitFaster.csproj
@@ -19,6 +19,7 @@
+
diff --git a/src/Features/PowerCSharp.Feature.Cache/CacheFeatureModule.cs b/src/Features/PowerCSharp.Feature.Cache/CacheFeatureModule.cs
index 7e33b5b..746665d 100644
--- a/src/Features/PowerCSharp.Feature.Cache/CacheFeatureModule.cs
+++ b/src/Features/PowerCSharp.Feature.Cache/CacheFeatureModule.cs
@@ -31,10 +31,14 @@ public void ConfigureServices(IFeatureRegistrationContext context)
{
// Force-load the abstractions assembly so the CLR can resolve its types during discovery.
_ = Assembly.Load("PowerCSharp.Feature.Cache.Abstractions");
-
+
context.Services.Configure(
context.Configuration.GetSection($"PowerFeatures:{Key}"));
+ // NOTE: context.Logger is NullLogger at this stage (the DI container isn't built yet, so
+ // no real ILoggerFactory exists). Anything logged here is silently discarded. The
+ // authoritative "which implementation actually won" diagnostic is logged for real in
+ // ConfigurePipeline below, once a real logger is available.
if (!context.Flags.IsEnabled(FeatureKey))
{
context.Logger.LogInformation("Cache feature disabled; registering NoOp cache services.");
@@ -45,4 +49,26 @@ public void ConfigureServices(IFeatureRegistrationContext context)
context.Services.TryAddSingleton();
context.Services.TryAddSingleton();
}
+
+ ///
+ ///
+ /// Diagnostic-only: resolves the services actually bound to and
+ /// from the fully-built container and logs their concrete
+ /// types with a real logger. This is the authoritative answer to "which cache implementation
+ /// is actually active" — unlike constructor-time logs from individual implementations (which
+ /// can fire for shadowed/overridden registrations too, e.g. under ASP.NET Core's
+ /// ValidateOnBuild in Development, without ever being the instance that's actually injected).
+ ///
+ public void ConfigurePipeline(IFeaturePipelineContext context)
+ {
+ var services = context.App.ApplicationServices;
+ var cache = services.GetRequiredService();
+ var diskCache = services.GetRequiredService();
+
+ context.Logger.LogInformation(
+ "Cache feature resolved -> ICacheService: {CacheImplementation}; IDiskCacheService: {DiskCacheImplementation}. " +
+ "A NoOp implementation here means no provider package overrode the safe-off floor for that contract.",
+ cache.GetType().FullName,
+ diskCache.GetType().FullName);
+ }
}