In-Memory Implementations
Foundatio provides in-memory implementations for all core abstractions. These are perfect for development, testing, and single-process applications.
Overview
| Implementation | Interface | Package |
|---|---|---|
InMemoryCacheClient | ICacheClient | Foundatio |
InMemoryQueue<T> | IQueue<T> | Foundatio |
InMemoryMessageBus | IMessageBus | Foundatio |
InMemoryFileStorage | IFileStorage | Foundatio |
CacheLockProvider | ILockProvider | Foundatio |
Installation
In-memory implementations are included in the core Foundatio package:
bash
dotnet add package FoundatioInMemoryCacheClient
A high-performance in-memory cache with optional LRU eviction.
Basic Usage
csharp
using Foundatio.Caching;
var cache = new InMemoryCacheClient();
// Store and retrieve values
await cache.SetAsync("key", "value");
var value = await cache.GetAsync<string>("key");
// With expiration
await cache.SetAsync("temp", "data", TimeSpan.FromMinutes(5));Configuration Options
csharp
var cache = new InMemoryCacheClient(options =>
{
// Maximum items (enables LRU eviction)
options.MaxItems = 1000;
// Clone values on get/set (thread safety)
options.CloneValues = true;
// How often to scan for expired items
options.ExpirationScanFrequency = TimeSpan.FromMinutes(1);
// Logger factory
options.LoggerFactory = loggerFactory;
// Time provider (useful for testing)
options.TimeProvider = TimeProvider.System;
});Features
- LRU Eviction: Automatically removes least recently used items when
MaxItemsis reached - Expiration: Items can have absolute or sliding expiration
- Value Cloning: Optionally clone values to prevent reference sharing issues
- Thread-Safe: All operations are thread-safe
DI Registration
csharp
// Simple registration
services.AddSingleton<ICacheClient, InMemoryCacheClient>();
// With configuration
services.AddSingleton<ICacheClient>(sp =>
new InMemoryCacheClient(options =>
{
options.MaxItems = 1000;
options.LoggerFactory = sp.GetRequiredService<ILoggerFactory>();
}));InMemoryQueue
A thread-safe in-memory queue with retry support and dead letter handling.
Basic Usage
csharp
using Foundatio.Queues;
var queue = new InMemoryQueue<WorkItem>();
// Enqueue items
await queue.EnqueueAsync(new WorkItem { Id = 1, Data = "Hello" });
// Dequeue and process
var entry = await queue.DequeueAsync();
if (entry != null)
{
// Process the item
Console.WriteLine(entry.Value.Data);
// Mark as complete
await entry.CompleteAsync();
}Configuration Options
csharp
var queue = new InMemoryQueue<WorkItem>(options =>
{
// Queue identifier
options.Name = "work-items";
// Work item timeout (for retry)
options.WorkItemTimeout = TimeSpan.FromMinutes(5);
// Retry settings
options.Retries = 3;
options.RetryDelay = TimeSpan.FromSeconds(30);
// Processing behaviors
options.Behaviors.Add(new EnqueueAbandonedQueueEntryBehavior());
// Logger
options.LoggerFactory = loggerFactory;
});Processing Patterns
csharp
// Continuous processing with handler
await queue.StartWorkingAsync(async (entry, token) =>
{
await ProcessWorkItemAsync(entry.Value);
});
// Process until empty
while (await queue.GetQueueStatsAsync() is { Queued: > 0 })
{
var entry = await queue.DequeueAsync();
await entry.CompleteAsync();
}DI Registration
csharp
services.AddSingleton<IQueue<WorkItem>>(sp =>
new InMemoryQueue<WorkItem>(options =>
{
options.Name = "work-items";
options.WorkItemTimeout = TimeSpan.FromMinutes(5);
options.LoggerFactory = sp.GetRequiredService<ILoggerFactory>();
}));InMemoryMessageBus
A simple in-memory pub/sub message bus for single-process communication.
Basic Usage
csharp
using Foundatio.Messaging;
var messageBus = new InMemoryMessageBus();
// Subscribe to messages
await messageBus.SubscribeAsync<UserCreatedEvent>(message =>
{
Console.WriteLine($"User created: {message.UserId}");
});
// Publish messages
await messageBus.PublishAsync(new UserCreatedEvent { UserId = "123" });Configuration Options
csharp
var messageBus = new InMemoryMessageBus(options =>
{
options.LoggerFactory = loggerFactory;
options.Serializer = serializer;
});Subscription Management
csharp
// Subscribe with options
await messageBus.SubscribeAsync<OrderEvent>(
handler: async (message, token) =>
{
await ProcessOrderAsync(message);
},
cancellationToken: stoppingToken);
// Type hierarchy subscription
await messageBus.SubscribeAsync<BaseEvent>(message =>
{
// Receives all events that inherit from BaseEvent
});DI Registration
csharp
services.AddSingleton<IMessageBus, InMemoryMessageBus>();
services.AddSingleton<IMessagePublisher>(sp =>
sp.GetRequiredService<IMessageBus>());
services.AddSingleton<IMessageSubscriber>(sp =>
sp.GetRequiredService<IMessageBus>());InMemoryFileStorage
An in-memory file storage implementation with optional size limits.
Basic Usage
csharp
using Foundatio.Storage;
var storage = new InMemoryFileStorage();
// Save files
await storage.SaveFileAsync("documents/file.txt", "Hello, World!");
// Read files
var content = await storage.GetFileContentsAsync("documents/file.txt");
// List files
var files = await storage.GetFileListAsync("documents/");Configuration Options
csharp
var storage = new InMemoryFileStorage(options =>
{
// Maximum number of files
options.MaxFiles = 1000;
// Maximum file size
options.MaxFileSize = 100 * 1024 * 1024; // 100MB
// Logger
options.LoggerFactory = loggerFactory;
// Serializer for metadata
options.Serializer = serializer;
});File Operations
csharp
// Save from stream
using var stream = File.OpenRead("local-file.txt");
await storage.SaveFileAsync("remote/file.txt", stream);
// Get file info
var spec = await storage.GetFileInfoAsync("remote/file.txt");
Console.WriteLine($"Size: {spec?.Size}, Modified: {spec?.Modified}");
// Check existence
if (await storage.ExistsAsync("remote/file.txt"))
{
// File exists
}
// Delete files
await storage.DeleteFileAsync("remote/file.txt");
await storage.DeleteFilesAsync("temp/"); // Delete by patternDI Registration
csharp
services.AddSingleton<IFileStorage>(sp =>
new InMemoryFileStorage(options =>
{
options.MaxFiles = 1000;
options.LoggerFactory = sp.GetRequiredService<ILoggerFactory>();
}));CacheLockProvider (In-Memory Locks)
Use CacheLockProvider with InMemoryCacheClient for in-memory distributed locks.
Basic Usage
csharp
using Foundatio.Lock;
var cache = new InMemoryCacheClient();
var messageBus = new InMemoryMessageBus();
var locker = new CacheLockProvider(cache, messageBus);
// Acquire a lock
await using var lockHandle = await locker.AcquireAsync("resource-key");
if (lockHandle != null)
{
// Do exclusive work
}Configuration Options
csharp
var locker = new CacheLockProvider(cache, messageBus, options =>
{
// Default lock duration
options.DefaultTimeToLive = TimeSpan.FromMinutes(5);
// Logger
options.LoggerFactory = loggerFactory;
});DI Registration
csharp
services.AddSingleton<ILockProvider>(sp =>
new CacheLockProvider(
sp.GetRequiredService<ICacheClient>(),
sp.GetRequiredService<IMessageBus>()));Complete In-Memory Setup
All Services
csharp
public static IServiceCollection AddFoundatioInMemory(
this IServiceCollection services)
{
// Cache
services.AddSingleton<ICacheClient, InMemoryCacheClient>();
// Message Bus
services.AddSingleton<IMessageBus, InMemoryMessageBus>();
services.AddSingleton<IMessagePublisher>(sp =>
sp.GetRequiredService<IMessageBus>());
services.AddSingleton<IMessageSubscriber>(sp =>
sp.GetRequiredService<IMessageBus>());
// Lock Provider
services.AddSingleton<ILockProvider>(sp =>
new CacheLockProvider(
sp.GetRequiredService<ICacheClient>(),
sp.GetRequiredService<IMessageBus>()));
// File Storage
services.AddSingleton<IFileStorage, InMemoryFileStorage>();
return services;
}
// With queues
public static IServiceCollection AddFoundatioQueue<T>(
this IServiceCollection services,
string name) where T : class
{
services.AddSingleton<IQueue<T>>(sp =>
new InMemoryQueue<T>(options =>
{
options.Name = name;
options.LoggerFactory = sp.GetRequiredService<ILoggerFactory>();
}));
return services;
}Usage
csharp
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddFoundatioInMemory();
builder.Services.AddFoundatioQueue<WorkItem>("work-items");
builder.Services.AddFoundatioQueue<EmailMessage>("emails");
var app = builder.Build();When to Use In-Memory
✅ Good Use Cases
- Local Development: No external dependencies needed
- Unit Testing: Fast, isolated tests
- Single-Process Apps: Simple deployments
- Prototyping: Quick iterations
- Small Workloads: Low-traffic applications
⚠️ Limitations
- No Persistence: Data lost on restart
- Single Process: No cross-process communication
- Memory Bound: Limited by available RAM
- No Clustering: Not suitable for distributed systems
Switching to Production
csharp
// Easy to swap implementations
if (builder.Environment.IsDevelopment())
{
services.AddSingleton<ICacheClient, InMemoryCacheClient>();
}
else
{
services.AddSingleton<ICacheClient>(sp =>
new RedisCacheClient(options =>
{
options.ConnectionMultiplexer =
ConnectionMultiplexer.Connect("redis:6379");
}));
}Testing with In-Memory
csharp
public class CacheTests
{
[Fact]
public async Task ShouldCacheAndRetrieveValue()
{
// Arrange
var cache = new InMemoryCacheClient();
// Act
await cache.SetAsync("key", "value");
var result = await cache.GetAsync<string>("key");
// Assert
Assert.Equal("value", result);
}
}Next Steps
- Redis Implementation - Production-ready distributed caching
- Azure Implementation - Cloud-native Azure services
- AWS Implementation - Amazon Web Services integration