CacheUtility 1.0.21

There is a newer version of this package available.
See the version list below for details.
dotnet add package CacheUtility --version 1.0.21
                    
NuGet\Install-Package CacheUtility -Version 1.0.21
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="CacheUtility" Version="1.0.21" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="CacheUtility" Version="1.0.21" />
                    
Directory.Packages.props
<PackageReference Include="CacheUtility" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add CacheUtility --version 1.0.21
                    
#r "nuget: CacheUtility, 1.0.21"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package CacheUtility@1.0.21
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=CacheUtility&version=1.0.21
                    
Install as a Cake Addin
#tool nuget:?package=CacheUtility&version=1.0.21
                    
Install as a Cake Tool

CacheUtility

A thread-safe, generic wrapper for System.Runtime.Caching that simplifies cache access and supports powerful caching patterns.

Overview

CacheUtility provides an easy-to-use abstraction over the standard .NET memory cache with additional features:

  • Automatic cache population
  • Various expiration strategies
  • Thread-safe operations
  • Support for cache groups
  • Dependency relationships between cache groups
  • Automatic background refresh functionality

Basic usage

Note: All examples assume you have added the using statement:

using CacheUtility;

Simple caching

The most common pattern is to request an item from the cache, providing a function to generate the item if it doesn't exist:

// Basic usage with default 30-minute sliding expiration
var result = Cache.Get("MyKey", "MyGroupName", () => 
{
    return MyLongRunningTask();
});

Caching with expiration

// With custom sliding expiration
var result = Cache.Get("MyKey", "MyGroupName", TimeSpan.FromHours(1), () => 
{
    return MyLongRunningTask();
});

// With absolute expiration
var result = Cache.Get("MyKey", "MyGroupName", DateTime.Now.AddDays(1), () => 
{
    return MyLongRunningTask();
});

Basic examples

Caching user data
// Cache user data with a sliding expiration
var userData = Cache.Get($"User_{userId}", "UserProfiles", TimeSpan.FromMinutes(30), () =>
{
    return database.GetUserById(userId);
});
Caching application settings
// Cache application settings with absolute expiration
var settings = Cache.Get("GlobalSettings", "AppConfig", DateTime.Now.AddHours(12), () =>
{
    return configurationService.LoadSettings();
});

Async operations

For async operations, you can use the utility with async/await:

var result = await Cache.Get("MyKey", "MyGroupName", async () => 
{
    return await MyLongRunningTaskAsync();
});

Cache management

Removing individual items

Remove a specific item from the cache:

Cache.Remove("MyKey", "MyGroupName");

Group operations

Remove an entire group of cached items:

Cache.RemoveGroup("MyGroupName");

Remove multiple groups:

Cache.RemoveGroup("GroupA", "GroupB", "GroupC");

Retrieving all items from a group

Get all cached items that belong to a specific group:

var allItems = Cache.GetAllByGroup("MyGroupName");

// Iterate through all items in the group
foreach (var kvp in allItems)
{
    Console.WriteLine($"Key: {kvp.Key}, Value: {kvp.Value}");
}

// Access specific items if you know the key
if (allItems.ContainsKey("MySpecificKey"))
{
    var specificItem = allItems["MySpecificKey"];
}

Cache metadata and monitoring

Get detailed metadata about cached items for monitoring, debugging, or displaying in management interfaces:

// Get metadata for all cached items
var allMetadata = Cache.GetAllCacheMetadata();

foreach (var metadata in allMetadata)
{
    Console.WriteLine($"Key: {metadata.CacheKey}");
    Console.WriteLine($"  Group: {metadata.GroupName}");
    Console.WriteLine($"  Type: {metadata.DataType}");
    Console.WriteLine($"  Size: {metadata.EstimatedMemorySize:N0} bytes");
    Console.WriteLine($"  Last Refresh: {metadata.LastRefreshTime:yyyy-MM-dd HH:mm:ss}");
    Console.WriteLine($"  Refresh Interval: {metadata.RefreshInterval}");
    Console.WriteLine($"  Is Refreshing: {metadata.IsRefreshing}");
    Console.WriteLine($"  Populate Method: {metadata.PopulateMethodName ?? "Unknown"}");
    
    if (metadata.CollectionCount.HasValue)
    {
        Console.WriteLine($"  Items in Collection: {metadata.CollectionCount}");
    }
}

// You can filter the results as needed
var userDataItems = allMetadata.Where(m => m.GroupName == "UserProfiles");
Available metadata properties

Each CacheItemMetadata object contains:

  • CacheKey: Original cache key (without group prefix)
  • GroupName: Cache group name
  • DataType: Type name of the cached object
  • EstimatedMemorySize: Estimated memory usage in bytes (using JSON serialization)
  • LastRefreshTime: When the data was last refreshed
  • LastRefreshAttempt: When the last refresh was attempted (regardless of success)
  • RefreshInterval: Auto-refresh interval
  • IsRefreshing: Whether a refresh operation is currently in progress
  • RefreshStartTime: When the current refresh operation started
  • CollectionCount: Number of items if the cached object is a collection
  • PopulateMethodName: Name of the method used to populate/refresh the cache item
  • RemovalCallbackName: Name of the removal callback method (currently not available due to MemoryCache limitations)
Populate method names

The PopulateMethodName property helps identify which methods are used to populate cache items:

// Direct method reference - shows actual method name
Cache.Get("key1", "group", MyDataService.LoadUserData);
// PopulateMethodName: "MyDataService.LoadUserData"

// Lambda expression - shows indicator
Cache.Get("key2", "group", () => database.GetUser(123));
// PopulateMethodName: "[Lambda/Anonymous]"

// Anonymous method - shows indicator  
Cache.Get("key3", "group", delegate() { return "test"; });
// PopulateMethodName: "[Lambda/Anonymous]"

This is particularly useful for:

  • Debugging: Identifying which populate methods are being called
  • Performance monitoring: Tracking which data sources are being used
  • Code analysis: Understanding cache usage patterns across your application
Use cases for metadata
  • Monitoring dashboards: Display cache usage, memory consumption, and refresh status
  • Debug interfaces: Inspect cache contents and timing information
  • Performance analysis: Identify large cached objects or frequently refreshed items
  • Administrative tools: Manage cache contents through custom interfaces
  • Reporting: Generate cache usage reports and statistics

Global cache operations

Clear the entire cache:

Cache.RemoveAll();

Clear the cache except for specific groups:

Cache.RemoveAllButThese(new List<string> { "CriticalData", "ApplicationSettings" });

Intermediate features

Removing multiple items

Remove multiple items that contain specific strings:

Cache.Remove(new List<string> { "UserProfile", "123" }, "UserData");
// This will remove any cache key containing both "UserProfile" and "123"

Working with multiple cached items

// Cache some user data
Cache.Get("User1", "UserData", () => GetUserInfo(1));
Cache.Get("User2", "UserData", () => GetUserInfo(2));
Cache.Get("User3", "UserData", () => GetUserInfo(3));

// Get all cached items from the group
var allUsers = Cache.GetAllByGroup("UserData");
Console.WriteLine($"Found {allUsers.Count} cached users");

// Process each cached item
foreach (var user in allUsers)
{
    Console.WriteLine($"User Key: {user.Key}, Data: {user.Value}");
}

Advanced features

Automatic data refresh

CacheUtility supports automatic background refresh of cached data at specified intervals. This feature ensures your cache stays up-to-date with fresh data while maintaining high performance by serving existing data immediately, even during refresh operations.

Key benefits:

  • Non-blocking: Cache calls return immediately with existing data, even when refresh is in progress
  • High availability: Your application remains responsive during slow data refresh operations
  • Automatic updates: Data stays fresh without manual intervention
  • Error resilient: Failed refreshes don't impact cache availability
Basic refresh usage
// Cache data with automatic refresh every 5 minutes
var userData = Cache.Get("user_123", "UserProfiles", 
    TimeSpan.FromHours(1), // Sliding expiration
    () => database.GetUserById(123), // Populate method
    refresh: TimeSpan.FromMinutes(5) // Refresh interval
);
Non-blocking behavior example
// Even if GetExpensiveData() takes 10 seconds to execute,
// subsequent cache calls will return immediately with existing data
var expensiveData = Cache.Get("expensive_key", "DataGroup",
    TimeSpan.FromMinutes(30),
    () => GetExpensiveDataFromAPI(), // Slow operation
    refresh: TimeSpan.FromMinutes(2)
);

// This call returns instantly, even if refresh is running in background
var sameData = Cache.Get("expensive_key", "DataGroup",
    TimeSpan.FromMinutes(30),
    () => GetExpensiveDataFromAPI(),
    refresh: TimeSpan.FromMinutes(2)
);
Real-world refresh scenarios

API data caching:

var weatherData = Cache.Get($"weather_{cityId}", "WeatherCache",
    TimeSpan.FromHours(2), // Cache for 2 hours max, after the cache item last has been accessed
    () => weatherAPI.GetCurrentWeather(cityId),
    refresh: TimeSpan.FromMinutes(15) // Refresh every 15 minutes
);

Database result caching:

var reports = Cache.Get("monthly_reports", "Reports",
    TimeSpan.FromHours(4),
    () => database.GenerateMonthlyReports(), // Expensive query
    refresh: TimeSpan.FromHours(1) // Refresh hourly
);

Configuration data:

var config = Cache.Get("app_config", "Configuration",
    TimeSpan.FromDays(1),
    () => configService.LoadConfiguration(),
    refresh: TimeSpan.FromMinutes(30) // Check for config updates every 30 minutes
);

Cache removal callbacks

CacheUtility supports optional removal callbacks that are invoked when cached items are removed from the cache. This is useful for cleanup operations, logging, or triggering dependent actions.

Basic removal callback
var result = Cache.Get("MyKey", "MyGroupName", 
    DateTime.Now.AddHours(1), // Either Absolute expiration
    TimeSpan.FromMinutes(10), // Or Sliding expiration
    CacheItemPriority.Default, // Priority
    () => MyLongRunningTask(),
    removedCallback: (args) => // Optional callback
    {
        Console.WriteLine($"Cache item removed. Key: {args.CacheItem.Key}, Reason: {args.RemovedReason}");
    });
Removal reasons

The callback provides a CacheEntryRemovedArguments object that contains:

  • CacheItem: The cache item that was removed
  • RemovedReason: The reason for removal (Removed, Expired, Evicted, ChangeMonitorChanged)

Common removal reasons:

  • Removed: Item was explicitly removed
  • Expired: Item expired (absolute or sliding expiration)
  • Evicted: Item was evicted due to memory pressure
  • ChangeMonitorChanged: Item was removed due to a dependency change
Practical callback examples

Cleanup resources:

var fileData = Cache.Get("FileData", "Files", 
    TimeSpan.FromMinutes(30), 
    CacheItemPriority.Default,
    () => LoadFileData("myfile.txt"),
    removedCallback: (args) =>
    {
        if (args.CacheItem.Value is IDisposable disposable)
        {
            disposable.Dispose();
        }
    });

Trigger dependent operations:

var config = Cache.Get("AppConfig", "Configuration", 
    DateTime.Now.AddHours(12), 
    () => LoadConfiguration(),
    removedCallback: (args) =>
    {
        // Refresh dependent services when configuration changes
        if (args.RemovedReason == CacheEntryRemovedReason.Expired)
        {
            RefreshDependentServices();
        }
    });

Cache dependencies

Set up dependencies between cache groups so that when one group is cleared, its dependent groups are also cleared:

// Set up dependencies
Cache.SetDependencies("ParentGroup", "ChildGroup1", "ChildGroup2");

// Now when ParentGroup is removed, ChildGroup1 and ChildGroup2 will also be removed
Cache.RemoveGroup("ParentGroup");
Cascading cache invalidation
// Set up dependencies
Cache.SetDependencies("UserData", "UserProfiles", "UserPreferences", "UserActivity");
Cache.SetDependencies("UserProfiles", "ProfilePhotos");

// Now when UserData is cleared, all dependent caches are also cleared
Cache.RemoveGroup("UserData");
// This will clear UserData, UserProfiles, ProfilePhotos, UserPreferences, and UserActivity

Best practices

  1. Group related items: Use meaningful group names to organize related cache items.
  2. Consider expiration strategies: Choose between sliding expiration (reset on access) and absolute expiration (fixed time) based on your use case.
  3. Set dependencies: Use cache dependencies to maintain consistency between related data.
  4. Use short keys: Keep your cache keys concise but descriptive.
  5. Choose appropriate refresh intervals:
    • Balance data freshness needs with system resources
    • Use longer intervals for stable data, shorter for rapidly changing data
    • Consider the cost of your populate method when setting refresh frequency
    • Remember that refresh happens in background, so cache remains available
  6. Use removal callbacks wisely:
    • Use callbacks for cleanup operations (disposing resources, closing connections)
    • Consider performance impact - callbacks are executed synchronously
    • Avoid heavy operations in callbacks to prevent blocking cache operations
    • Use callbacks for logging and monitoring cache behavior

Performance considerations

  • The CacheUtility uses locks to ensure thread safety, but is designed to minimize lock contention.
  • Populate methods are only called once per cache miss, even under high concurrency.
  • Refresh operations are non-blocking: Cache calls return immediately with existing data, even during background refresh.
  • Background refresh uses Task.Run() to prevent blocking the main thread.
  • Multiple concurrent refresh requests for the same cache key are automatically deduplicated.
  • Consider memory usage when caching large objects or collections.

When to use cache groups vs. key prefixes

  • Cache groups: Use when you need to invalidate multiple related items at once.
  • Key prefixes: Use within your keys when you want to organize items but may need more granular control.

Memory management

The CacheUtility is built on top of .NET's MemoryCache, which has built-in memory pressure detection. However, be mindful of:

  • Setting appropriate cache priorities
  • Using reasonable expiration times
  • Caching only necessary data

Thread safety

All operations in CacheUtility are thread-safe. The implementation uses ReaderWriterLockSlim for efficient concurrent access and CacheLock for synchronizing modifications to the cache.

Product Compatible and additional computed target framework versions.
.NET net9.0 is compatible.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.2.0 175 9/1/2025
1.1.0 133 8/22/2025
1.0.21 158 8/21/2025
1.0.20 165 8/13/2025
1.0.10 132 7/12/2025
1.0.9 171 6/19/2025
1.0.8 161 6/19/2025
1.0.6 157 6/19/2025
1.0.5 213 4/20/2025
1.0.4 240 4/16/2025
1.0.3 185 11/7/2024
1.0.2 142 11/7/2024
1.0.1 665 9/3/2023
1.0.0 649 9/3/2023

Added GetAllCacheMetadata() method for monitoring and debugging. Returns detailed metadata including memory usage, populate method names, refresh status, and collection counts. Perfect for admin dashboards and performance analysis. Fully backward compatible.