Skip to content

In-Memory Implementations

Foundatio provides in-memory implementations for all core abstractions. These are perfect for development, testing, and single-process applications.

Overview

ImplementationInterfacePackage
InMemoryCacheClientICacheClientFoundatio
InMemoryQueue<T>IQueue<T>Foundatio
InMemoryMessageBusIMessageBusFoundatio
InMemoryFileStorageIFileStorageFoundatio
CacheLockProviderILockProviderFoundatio

Installation

In-memory implementations are included in the core Foundatio package:

bash
dotnet add package Foundatio

InMemoryCacheClient

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 MaxItems is 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 pattern

DI 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

Released under the Apache 2.0 License.