### Install dotnet-postgres-cache Tool Source: https://github.com/azure/microsoft.extensions.caching.postgres/blob/main/test/README.md Installs the global tool for managing the Postgres cache. Specify a version if needed for pre-release versions. ```bash dotnet tool install --global dotnet-postgres-cache ``` -------------------------------- ### Install Microsoft.Extensions.Caching.Postgres NuGet Package Source: https://github.com/azure/microsoft.extensions.caching.postgres/blob/main/src/README.md Use this command to add the PostgreSQL distributed cache package to your .NET project. ```powershell dotnet add package Microsoft.Extensions.Caching.Postgres ``` -------------------------------- ### Integrate HybridCache with PostgresCache Source: https://context7.com/azure/microsoft.extensions.caching.postgres/llms.txt Integrate PostgreSQL as the distributed backing store for HybridCache, combining in-memory L1 cache with durable PostgreSQL L2 cache. This setup requires registering the distributed cache and then adding HybridCache to the services. ```csharp using Microsoft.Extensions.Caching.Hybrid; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; var host = Host.CreateDefaultBuilder(args) .ConfigureServices((context, services) => { // Register PostgreSQL as the distributed cache services.AddDistributedPostgresCache(options => { options.ConnectionString = context.Configuration.GetConnectionString("PostgresCache"); options.SchemaName = "public"; options.TableName = "__hybrid_cache"; options.CreateIfNotExists = true; }); // Add HybridCache which automatically uses the registered IDistributedCache services.AddHybridCache(); }) .Build(); // Service using HybridCache public class DataService { private readonly HybridCache _cache; public DataService(HybridCache cache) { _cache = cache; } public async Task> GetProductsAsync(string category, CancellationToken cancellationToken = default) { var options = new HybridCacheEntryOptions { LocalCacheExpiration = TimeSpan.FromSeconds(30), // L1 in-memory cache Expiration = TimeSpan.FromMinutes(5) // L2 PostgreSQL cache }; return await _cache.GetOrCreateAsync( $"products:{category}", async ct => await LoadProductsFromDatabaseAsync(category, ct), options, cancellationToken: cancellationToken); } private async Task> LoadProductsFromDatabaseAsync(string category, CancellationToken ct) { // Expensive database query await Task.Delay(100, ct); // Simulated delay return new[] { new Product("1", "Widget", 9.99m) }; } } public record Product(string Id, string Name, decimal Price); // Usage - First call hits database, subsequent calls hit L1/L2 cache var service = host.Services.GetRequiredService(); var products1 = await service.GetProductsAsync("electronics"); // Database + cache write var products2 = await service.GetProductsAsync("electronics"); // L1 memory cache hit // After 30s: L2 PostgreSQL cache hit // After 5min: Database query again ``` -------------------------------- ### Using IDistributedCache with Postgres Source: https://github.com/azure/microsoft.extensions.caching.postgres/blob/main/src/README.md Inject and use the `IDistributedCache` interface to perform cache operations like getting and setting data. This example demonstrates fetching data, caching it with specific expiration options, and returning cached data if available. ```csharp using Microsoft.Extensions.Caching.Distributed; public class MyService { private readonly IDistributedCache _cache; public MyService(IDistributedCache cache) { _cache = cache; } public async Task GetDataAsync(string key) { var cachedData = await _cache.GetStringAsync(key); if (cachedData == null) { // Fetch data from source var data = await FetchDataFromSource(); // Cache the data with options var options = new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30), SlidingExpiration = TimeSpan.FromMinutes(5) }; await _cache.SetStringAsync(key, data, options); return data; } return cachedData; } } ``` -------------------------------- ### Register Postgres Distributed Cache Service Source: https://github.com/azure/microsoft.extensions.caching.postgres/blob/main/src/README.md Register the distributed PostgreSQL cache service using configuration binding in your .NET application's service collection. This example shows how to map configuration values to cache options. ```csharp using Microsoft.Extensions.DependencyInjection; var builder = WebApplication.CreateBuilder(args); // Register Postgres distributed cache builder.Services.AddDistributedPostgresCache(options => { options.ConnectionString = builder.Configuration.GetConnectionString("PostgresCache"); options.SchemaName = builder.Configuration.GetValue("PostgresCache:SchemaName", "public"); options.TableName = builder.Configuration.GetValue("PostgresCache:TableName", "cache"); options.CreateIfNotExists = builder.Configuration.GetValue("PostgresCache:CreateIfNotExists", true); options.UseWAL = builder.Configuration.GetValue("PostgresCache:UseWAL", false); // Optional: Configure expiration settings var expirationInterval = builder.Configuration.GetValue("PostgresCache:ExpiredItemsDeletionInterval"); if (!string.IsNullOrEmpty(expirationInterval) && TimeSpan.TryParse(expirationInterval, out var interval)) { options.ExpiredItemsDeletionInterval = interval; } var slidingExpiration = builder.Configuration.GetValue("PostgresCache:DefaultSlidingExpiration"); if (!string.IsNullOrEmpty(slidingExpiration) && TimeSpan.TryParse(slidingExpiration, out var sliding)) { options.DefaultSlidingExpiration = sliding; } }); var app = builder.Build(); ``` -------------------------------- ### Configure appsettings.json for Postgres Cache Source: https://github.com/azure/microsoft.extensions.caching.postgres/blob/main/src/README.md Define the connection string and cache-specific settings in your `appsettings.json` file. Ensure connection pooling is enabled for better performance. ```json "ConnectionStrings": { "PostgresCache": "Host=localhost;Port=5432;Username=postgres;Password=yourpassword;Database=yourdatabase;Pooling=true;MinPoolSize=0;MaxPoolSize=100;Timeout=15;" }, "PostgresCache": { "SchemaName": "public", "TableName": "cache", "CreateIfNotExists": true, "UseWAL": false, "ExpiredItemsDeletionInterval": "00:30:00", "DefaultSlidingExpiration": "00:20:00" } ``` -------------------------------- ### Configure appsettings.json for PostgreSQL Cache Source: https://github.com/azure/microsoft.extensions.caching.postgres/blob/main/README.md Define connection strings and cache-specific settings in your appsettings.json file. Ensure connection pooling is enabled for performance. ```json { "ConnectionStrings": { "PostgresCache": "Host=localhost;Port=5432;Username=postgres;Password=yourpassword;Database=yourdatabase;Pooling=true;MinPoolSize=0;MaxPoolSize=100;Timeout=15;" }, "PostgresCache": { "SchemaName": "public", "TableName": "cache", "CreateIfNotExists": true, "UseWAL": false, "ExpiredItemsDeletionInterval": "00:30:00", "DefaultSlidingExpiration": "00:20:00" } } ``` -------------------------------- ### Run Postgres Cache Tests Source: https://github.com/azure/microsoft.extensions.caching.postgres/blob/main/test/README.md Executes the Postgres cache tests against a specified database. Ensure the connection string points to an existing database. ```bash dotnet postgres-cache [connectionstring] public CacheTest false ``` -------------------------------- ### Configure Postgres Cache using Environment Variables Source: https://github.com/azure/microsoft.extensions.caching.postgres/blob/main/src/README.md Configure the PostgreSQL distributed cache by setting environment variables with the `PostgresCache__` prefix. This is an alternative to using `appsettings.json`. ```bash PostgresCache__ConnectionString="Host=localhost;Port=5432;Username=postgres;Password=yourpassword;Database=yourdatabase" PostgresCache__SchemaName="public" PostgresCache__TableName="cache" PostgresCache__CreateIfNotExists="true" ``` -------------------------------- ### Configure DistributedPostgresCache Options Source: https://context7.com/azure/microsoft.extensions.caching.postgres/llms.txt Configure the distributed PostgreSQL cache with connection string, schema, table name, and optional settings like auto-creation, WAL usage, and expiration intervals. Ensure the connection string is valid and the schema/table names are appropriate for your PostgreSQL instance. ```csharp using Microsoft.Extensions.Caching.Postgres; using Microsoft.Extensions.DependencyInjection; using Npgsql; var builder = WebApplication.CreateBuilder(args); builder.Services.AddDistributedPostgresCache(options => { // Required: Connection string to PostgreSQL database options.ConnectionString = "Host=localhost;Port=5432;Username=postgres;Password=secret;Database=myapp;Pooling=true;MaxPoolSize=100;"; // Required: Schema and table configuration options.SchemaName = "cache_schema"; // PostgreSQL schema name options.TableName = "distributed_cache"; // Table name for cache entries // Optional: Auto-create table on startup (default: false) options.CreateIfNotExists = true; // Optional: Use UNLOGGED table for better performance (default: false) // UNLOGGED tables are faster but data is lost on crash options.UseWAL = false; // Optional: Interval for cleaning up expired items (default: 30 minutes, minimum: 5 minutes) options.ExpiredItemsDeletionInterval = TimeSpan.FromMinutes(15); // Optional: Default sliding expiration when none specified (default: 20 minutes) options.DefaultSlidingExpiration = TimeSpan.FromMinutes(10); // Optional: Provide an existing NpgsqlDataSource (highest precedence) // Useful when sharing a data source across your application // options.DataSource = existingDataSource; // Optional: Custom TimeProvider for testing // options.TimeProvider = new FakeTimeProvider(); }); // Alternative: Using existing NpgsqlDataSource var dataSource = NpgsqlDataSource.Create(connectionString); builder.Services.AddSingleton(dataSource); builder.Services.AddDistributedPostgresCache(options => { options.DataSource = dataSource; // Uses existing data source options.SchemaName = "public"; options.TableName = "cache"; }); var app = builder.Build(); ``` -------------------------------- ### Store and Retrieve String Values Directly Source: https://context7.com/azure/microsoft.extensions.caching.postgres/llms.txt Utilize GetStringAsync and SetStringAsync for convenient handling of string data in the cache, avoiding manual encoding and decoding. ```csharp using Microsoft.Extensions.Caching.Distributed; public class SessionService { private readonly IDistributedCache _cache; public SessionService(IDistributedCache cache) { _cache = cache; } public async Task GetSessionDataAsync(string sessionId) { string cacheKey = $"session:{sessionId}"; // Retrieve string directly string? sessionJson = await _cache.GetStringAsync(cacheKey); return sessionJson; } public async Task SetSessionDataAsync(string sessionId, string sessionJson) { string cacheKey = $"session:{sessionId}"; var options = new DistributedCacheEntryOptions { SlidingExpiration = TimeSpan.FromMinutes(30), AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(8) }; // Store string directly await _cache.SetStringAsync(cacheKey, sessionJson, options); } } // Usage var sessionService = serviceProvider.GetRequiredService(); await sessionService.SetSessionDataAsync("abc123", "{\"userId\":42,\"role\":\"admin\"}"); var data = await sessionService.GetSessionDataAsync("abc123"); // Returns: "{\"userId\":42,\"role\":\"admin\"}" ``` -------------------------------- ### Cache-Aside Pattern with JSON Serialization Source: https://context7.com/azure/microsoft.extensions.caching.postgres/llms.txt Implements the cache-aside pattern for fetching, updating, and deleting entities, utilizing JSON serialization for cache storage. Requires `IDistributedCache` and a base repository. ```csharp using Microsoft.Extensions.Caching.Distributed; using System.Text.Json; public class CachedRepository where T : class { private readonly IDistributedCache _cache; private readonly IRepository _repository; private readonly TimeSpan _defaultExpiration; private readonly JsonSerializerOptions _jsonOptions; public CachedRepository( IDistributedCache cache, IRepository repository, TimeSpan? defaultExpiration = null) { _cache = cache; _repository = repository; _defaultExpiration = defaultExpiration ?? TimeSpan.FromMinutes(15); _jsonOptions = new JsonSerializerOptions { PropertyNameCaseInsensitive = true }; } public async Task GetByIdAsync(string id, CancellationToken cancellationToken = default) { string cacheKey = $"{typeof(T).Name}:{id}"; // Try cache first var cachedJson = await _cache.GetStringAsync(cacheKey, cancellationToken); if (cachedJson is not null) { return JsonSerializer.Deserialize(cachedJson, _jsonOptions); } // Cache miss - fetch from repository var entity = await _repository.GetByIdAsync(id, cancellationToken); if (entity is null) { return null; } // Populate cache var options = new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = _defaultExpiration }; var json = JsonSerializer.Serialize(entity, _jsonOptions); await _cache.SetStringAsync(cacheKey, json, options, cancellationToken); return entity; } public async Task UpdateAsync(T entity, string id, CancellationToken cancellationToken = default) { // Update repository var updated = await _repository.UpdateAsync(entity, cancellationToken); // Invalidate cache string cacheKey = $"{typeof(T).Name}:{id}"; await _cache.RemoveAsync(cacheKey, cancellationToken); return updated; } public async Task DeleteAsync(string id, CancellationToken cancellationToken = default) { // Delete from repository await _repository.DeleteAsync(id, cancellationToken); // Invalidate cache string cacheKey = $"{typeof(T).Name}:{id}"; await _cache.RemoveAsync(cacheKey, cancellationToken); } } ``` ```csharp // Usage in DI registration builder.Services.AddScoped(typeof(CachedRepository<>)); builder.Services.AddScoped, CustomerRepository>(); ``` ```csharp // Usage in controller public class CustomersController : ControllerBase { private readonly CachedRepository _customers; public CustomersController(CachedRepository customers) { _customers = customers; } [HttpGet("{id}")] public async Task> Get(string id) { var customer = await _customers.GetByIdAsync(id); return customer is null ? NotFound() : Ok(customer); } } ``` -------------------------------- ### Register PostgreSQL Distributed Cache Services Source: https://context7.com/azure/microsoft.extensions.caching.postgres/llms.txt Registers the PostgreSQL distributed cache with dependency injection. Configure connection details, schema, table name, and expiration policies. Supports auto-creation of tables and UNLOGGED tables for performance. Advanced options include Azure Entra ID authentication via NpgsqlDataSourceBuilder. ```csharp using Microsoft.Extensions.DependencyInjection; var builder = WebApplication.CreateBuilder(args); // Basic registration with configuration builder.Services.AddDistributedPostgresCache(options => { options.ConnectionString = "Host=localhost;Port=5432;Username=postgres;Password=yourpassword;Database=mydb;Pooling=true;MinPoolSize=0;MaxPoolSize=100;"; options.SchemaName = "public"; options.TableName = "cache"; options.CreateIfNotExists = true; // Auto-create table on first use options.UseWAL = false; // Use UNLOGGED table for better performance options.ExpiredItemsDeletionInterval = TimeSpan.FromMinutes(30); options.DefaultSlidingExpiration = TimeSpan.FromMinutes(20); }); // Advanced registration with NpgsqlDataSourceBuilder for Azure Entra authentication builder.Services.AddDistributedPostgresCache( options => { options.ConnectionString = builder.Configuration.GetConnectionString("PostgresCache"); options.SchemaName = "public"; options.TableName = "cache"; options.CreateIfNotExists = true; }, dataSourceBuilder => { // Configure advanced authentication, plugins, or custom settings dataSourceBuilder.UsePeriodicPasswordProvider( async (_, ct) => await GetAzureAccessTokenAsync(ct), TimeSpan.FromMinutes(55), TimeSpan.FromSeconds(5)); }); var app = builder.Build(); ``` -------------------------------- ### Store Cached Values with Expiration Options Source: https://context7.com/azure/microsoft.extensions.caching.postgres/llms.txt Use SetAsync to store a serialized object in the cache. Configure sliding expiration, absolute expiration, or a combination of both using DistributedCacheEntryOptions. ```csharp using Microsoft.Extensions.Caching.Distributed; using System.Text.Json; public class ProductService { private readonly IDistributedCache _cache; public ProductService(IDistributedCache cache) { _cache = cache; } public async Task CacheProductAsync(Product product, CancellationToken cancellationToken = default) { string cacheKey = $"product:{product.Id}"; byte[] serializedProduct = JsonSerializer.SerializeToUtf8Bytes(product); // Option 1: Sliding expiration only (resets each time item is accessed) var slidingOptions = new DistributedCacheEntryOptions { SlidingExpiration = TimeSpan.FromMinutes(10) }; await _cache.SetAsync(cacheKey, serializedProduct, slidingOptions, cancellationToken); // Option 2: Absolute expiration only (expires at fixed time) var absoluteOptions = new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1) }; await _cache.SetAsync(cacheKey, serializedProduct, absoluteOptions, cancellationToken); // Option 3: Both sliding and absolute (sliding resets, but never exceeds absolute) var combinedOptions = new DistributedCacheEntryOptions { SlidingExpiration = TimeSpan.FromMinutes(5), AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30) }; await _cache.SetAsync(cacheKey, serializedProduct, combinedOptions, cancellationToken); } public void CacheProductSync(Product product) { string cacheKey = $"product:{product.Id}"; byte[] serializedProduct = JsonSerializer.SerializeToUtf8Bytes(product); var options = new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(30) }; _cache.Set(cacheKey, serializedProduct, options); } } public record Product(string Id, string Name, decimal Price); // Usage var product = new Product("12345", "Widget", 29.99m); await service.CacheProductAsync(product); ``` -------------------------------- ### Retrieve Cached Values Asynchronously Source: https://context7.com/azure/microsoft.extensions.caching.postgres/llms.txt Retrieves a cached value by key asynchronously. Returns null if the key doesn't exist or has expired. Automatically refreshes sliding expiration when accessed. Requires `IDistributedCache` to be registered. ```csharp using Microsoft.Extensions.Caching.Distributed; using System.Text.Json; public class ProductService { private readonly IDistributedCache _cache; public ProductService(IDistributedCache cache) { _cache = cache; } public async Task GetProductAsync(string productId, CancellationToken cancellationToken = default) { string cacheKey = $"product:{productId}"; // Async retrieval (recommended) byte[]? cachedBytes = await _cache.GetAsync(cacheKey, cancellationToken); if (cachedBytes is not null) { return JsonSerializer.Deserialize(cachedBytes); } return null; } public Product? GetProductSync(string productId) { string cacheKey = $"product:{productId}"; // Synchronous retrieval byte[]? cachedBytes = _cache.Get(cacheKey); if (cachedBytes is not null) { return JsonSerializer.Deserialize(cachedBytes); } return null; } } // Usage var service = serviceProvider.GetRequiredService(); var product = await service.GetProductAsync("12345"); // Returns: Product object or null if not cached ``` -------------------------------- ### GetOrCreateAsync - Atomic Cache Population Source: https://context7.com/azure/microsoft.extensions.caching.postgres/llms.txt Retrieves a cached value or creates it atomically using a PostgreSQL advisory lock to prevent cache stampedes. This method is specific to PostgresCache. Ensure IDistributedCache is injected and cast to PostgresCache. ```csharp using Microsoft.Extensions.Caching.Distributed; using Microsoft.Extensions.Caching.Postgres; using System.Text.Json; public class ExpensiveDataService { private readonly PostgresCache _cache; private readonly IExternalApiClient _apiClient; public ExpensiveDataService(IDistributedCache cache, IExternalApiClient apiClient) { // GetOrCreateAsync is specific to PostgresCache _cache = (PostgresCache)cache; _apiClient = apiClient; } public async Task GetWeatherDataAsync(string city, CancellationToken cancellationToken = default) { string cacheKey = $"weather:{city}"; var options = new DistributedCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(15) }; // Atomic get-or-create with advisory lock prevents cache stampede byte[] cachedBytes = await _cache.GetOrCreateAsync( cacheKey, async ct => { // This factory only runs if cache miss occurs // Advisory lock ensures only ONE caller executes this var weatherData = await _apiClient.FetchWeatherAsync(city, ct); return JsonSerializer.SerializeToUtf8Bytes(weatherData); }, options, cancellationToken); return JsonSerializer.Deserialize(cachedBytes)!; } } public record WeatherData(string City, double Temperature, string Conditions); // Usage - Multiple concurrent calls for same key only trigger ONE API call var tasks = Enumerable.Range(0, 100) .Select(_ => service.GetWeatherDataAsync("Seattle")) .ToArray(); var results = await Task.WhenAll(tasks); // Only one FetchWeatherAsync call is made; others wait for cached result ``` -------------------------------- ### RefreshAsync / Refresh - Extend Sliding Expiration Source: https://context7.com/azure/microsoft.extensions.caching.postgres/llms.txt Resets the sliding expiration timeout for a cache entry without retrieving its value. Useful for keeping items alive when you know they're still relevant. Ensure IDistributedCache is injected. ```csharp using Microsoft.Extensions.Caching.Distributed; public class SessionService { private readonly IDistributedCache _cache; public SessionService(IDistributedCache cache) { _cache = cache; } public async Task KeepSessionAliveAsync(string sessionId, CancellationToken cancellationToken = default) { string cacheKey = $"session:{sessionId}"; // Refresh sliding expiration without retrieving value await _cache.RefreshAsync(cacheKey, cancellationToken); } public void KeepSessionAliveSync(string sessionId) { string cacheKey = $"session:{sessionId}"; // Synchronous refresh _cache.Refresh(cacheKey); } } // Usage in middleware public class SessionRefreshMiddleware { private readonly RequestDelegate _next; public SessionRefreshMiddleware(RequestDelegate next) { _next = next; } public async Task InvokeAsync(HttpContext context, IDistributedCache cache) { if (context.Request.Cookies.TryGetValue("SessionId", out var sessionId)) { // Keep session alive on each request without loading session data await cache.RefreshAsync($"session:{sessionId}"); } await _next(context); } } ``` -------------------------------- ### RemoveAsync / Remove - Delete Cached Items Source: https://context7.com/azure/microsoft.extensions.caching.postgres/llms.txt Removes a specific item from the cache by key. Use the asynchronous RemoveAsync for better performance. Ensure the IDistributedCache interface is injected into your service. ```csharp using Microsoft.Extensions.Caching.Distributed; public class ProductService { private readonly IDistributedCache _cache; public ProductService(IDistributedCache cache) { _cache = cache; } public async Task InvalidateProductCacheAsync(string productId, CancellationToken cancellationToken = default) { string cacheKey = $"product:{productId}"; // Async removal (recommended) await _cache.RemoveAsync(cacheKey, cancellationToken); } public void InvalidateProductCacheSync(string productId) { string cacheKey = $"product:{productId}"; // Synchronous removal _cache.Remove(cacheKey); } public async Task UpdateProductAsync(Product product, CancellationToken cancellationToken = default) { // Update in database await _productRepository.UpdateAsync(product, cancellationToken); // Invalidate cache to ensure fresh data on next read await InvalidateProductCacheAsync(product.Id, cancellationToken); } } // Usage await service.InvalidateProductCacheAsync("12345"); // Cache entry is removed; next GetAsync returns null ``` === COMPLETE CONTENT === This response contains all available snippets from this library. No additional content exists. Do not make further requests.