Shuttle.Recall 22.0.0-beta.1

Prefix Reserved
This is a prerelease version of Shuttle.Recall.
There is a newer version of this package available.
See the version list below for details.
dotnet add package Shuttle.Recall --version 22.0.0-beta.1
                    
NuGet\Install-Package Shuttle.Recall -Version 22.0.0-beta.1
                    
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="Shuttle.Recall" Version="22.0.0-beta.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Shuttle.Recall" Version="22.0.0-beta.1" />
                    
Directory.Packages.props
<PackageReference Include="Shuttle.Recall" />
                    
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 Shuttle.Recall --version 22.0.0-beta.1
                    
#r "nuget: Shuttle.Recall, 22.0.0-beta.1"
                    
#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 Shuttle.Recall@22.0.0-beta.1
                    
#: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=Shuttle.Recall&version=22.0.0-beta.1&prerelease
                    
Install as a Cake Addin
#tool nuget:?package=Shuttle.Recall&version=22.0.0-beta.1&prerelease
                    
Install as a Cake Tool

Shuttle.Recall

Shuttle.Recall is an event-sourcing mechanism for .NET that provides a flexible way to persist and retrieve event streams.

Installation

dotnet add package Shuttle.Recall

Registration

To register Shuttle.Recall, use the AddRecall extension method:

services.AddRecall(builder => 
{
    builder.AddProjection("ProjectionName", projection =>
    {
        projection.AddEventHandler<SomeEvent>((context, evt) => 
        {
            // handle event
        });
    });
});

The following types are registered:

  • IEventStore (Scoped): Used to retrieve and save event streams.
  • IEventProcessor (Singleton): Used to process projections.
  • IEventMethodInvoker (Singleton): Invokes event handling methods on aggregate roots.
  • ISerializer (Singleton): Serializes and deserializes events.
  • IConcurrencyExceptionSpecification (Singleton): Detects concurrency exceptions.

Configuration Options

services.AddRecall(options =>
{
    options.EventProcessing.ProjectionThreadCount = 5;
    options.EventProcessing.IncludedProjections.Add("ProjectionName");
    options.EventProcessing.ExcludedProjections.Add("ExcludeMe");

    options.EventProcessing.ImmediateConsistency.Enabled = true;
    options.EventProcessing.ImmediateConsistency.IncludedProjections.Add("ProjectionName");

    options.EventStore.CompressionAlgorithm = "gzip";
    options.EventStore.EncryptionAlgorithm = "aes";
});

EventProcessing Options

Property Default Description
ProjectionThreadCount 5 Number of threads for projection processing
IncludedProjections [] List of projection names to include
ExcludedProjections [] List of projection names to exclude
ProjectionProcessorIdleDurations varies Idle durations for processor polling
ImmediateConsistency see below Options controlling immediate consistency processing
ImmediateConsistencyFailed AsyncEvent<ImmediateConsistencyFailedEventArgs> raised when a projection handler throws while processing an event immediately

ImmediateConsistency Options

By default, a projection only ever processes an event once the background IEventProcessor gets round to it. Immediate consistency lets specific projections handle an event synchronously, as part of the IEventStore.SaveAsync call that persisted it, so that a read model built from that projection is guaranteed to reflect the event by the time SaveAsync returns.

Property Default Description
Enabled false While false, every save is processed eventually only and IncludedProjections/ExcludedProjections below are ignored
IncludedProjections [] Projection names that should be handled immediately. If empty, every registered projection is eligible (subject to ExcludedProjections). May not be specified together with ExcludedProjections
ExcludedProjections [] Projection names to exclude from immediate handling when IncludedProjections is empty. May not be specified together with IncludedProjections

These IncludedProjections/ExcludedProjections only decide which projections are handled immediately — they are separate from, and have no effect on, the EventProcessing.IncludedProjections/ExcludedProjections above, which decide which projections the eventual IEventProcessor handles at all.

If a projection's handler throws while being invoked immediately, the event is not lost: EventProcessing.ImmediateConsistencyFailed is raised, and the eventual IEventProcessor will still pick up and retry the event on its next pass.

options.EventProcessing.ImmediateConsistencyFailed += (args, cancellationToken) =>
{
    _logger.LogWarning("Projection '{ProjectionName}' failed to handle event '{EventId}' immediately: {Exception}", args.ProjectionName, args.PrimitiveEvent.EventId, args.Exception);

    return Task.CompletedTask;
};

You can also request immediate consistency for a single SaveAsync call, regardless of whether Enabled is set — see Saving with Immediate Consistency.

EventStore Options

Property Default Description
CompressionAlgorithm "" Compression algorithm (e.g., "gzip")
EncryptionAlgorithm "" Encryption algorithm (e.g., "aes")
EventHandlingMethodName "On" Method name invoked on aggregate roots
BindingFlags Instance \| NonPublic Binding flags for event method discovery

Usage

Saving an Event Stream

var eventStore = serviceProvider.GetRequiredService<IEventStore>();
var streamId = Guid.NewGuid();
var stream = await eventStore.GetAsync(streamId);

stream.Add(new SomeEvent { Data = "example" });

await eventStore.SaveAsync(stream);

Saving with Headers

var stream = await eventStore.GetAsync(streamId, builder =>
{
    builder.AddHeader("key", "value");
});

stream.Add(new SomeEvent { Data = "example" });

await eventStore.SaveAsync(stream);

Saving with Concurrency Check

var stream = await eventStore.GetAsync(streamId);

stream.Add(new SomeEvent { Data = "example" });

stream.ConcurrencyInvariant(5); // throws EventStreamConcurrencyException if version != 5

await eventStore.SaveAsync(stream);

Saving with Correlation ID

var stream = await eventStore.GetAsync(streamId);

stream
    .WithCorrelationId(correlationId)
    .Add(new SomeEvent { Data = "example" });

await eventStore.SaveAsync(stream);

Saving with Immediate Consistency

var stream = await eventStore.GetAsync(streamId);

stream.Add(new SomeEvent { Data = "example" });

await eventStore.SaveAsync(stream, builder =>
{
    builder.WithImmediateConsistency();
});

This requests immediate consistency for this save only, even when EventProcessing.ImmediateConsistency.Enabled is false. Which projections actually run immediately is still governed by EventProcessing.ImmediateConsistency.IncludedProjections/ExcludedProjections — see ImmediateConsistency Options.

Retrieving an Event Stream

var stream = await eventStore.GetAsync(streamId);

// Apply committed events to an aggregate root or state object
stream.Apply(someAggregateRoot);

Retrieving Events by Type

var stream = await eventStore.GetAsync(streamId);

// Get only committed events
var committedEvents = stream.GetEvents(EventStream.EventRegistrationType.Committed);

// Get only appended events
var appendedEvents = stream.GetEvents(EventStream.EventRegistrationType.Appended);

// Get all events
var allEvents = stream.GetEvents(EventStream.EventRegistrationType.All);

Committing Events Before Apply

var stream = await eventStore.GetAsync(streamId);

stream.Add(new SomeEvent { Data = "example" });

// Events are only applied after commit
stream.Commit();

// Now Apply() will include the committed events
stream.Apply(someAggregateRoot);

Removing an Event Stream

var stream = await eventStore.GetAsync(streamId);

stream.Remove();

await eventStore.RemoveAsync(streamId);

Projections

Handler Implementation

Implement the IEventHandler<T> interface to handle events:

public class OrderProjection : IEventHandler<OrderPlaced>
{
    public async Task HandleAsync(IEventHandlerContext<OrderPlaced> context, CancellationToken cancellationToken = default)
    {
        var evt = context.Event;
        var projection = context.Projection;
        var primitiveEvent = context.PrimitiveEvent;

        // Process the event
        await SaveToReadModelAsync(evt.OrderId, evt.Amount, cancellationToken);

        // Optionally defer for retry
        // context.Defer(TimeSpan.FromSeconds(5));
    }
}

Registering Projections

services.AddRecall(builder => 
{
    builder.AddProjection("OrderProjection", projection =>
    {
        projection.AddEventHandler<OrderProjection>();
    });
});

Inline Projection Handlers

services.AddRecall(builder => 
{
    builder.AddProjection("OrderProjection", projection =>
    {
        projection.AddEventHandler((IEventHandlerContext<OrderPlaced> context) =>
        {
            var evt = context.Event;
            // handle event inline
        });
    });
});

Delegate-based Handlers

builder.AddProjection("ProjectionName", (IEventHandlerContext<SomeEvent> context) =>
{
    // handle event
});

IEventProcessor Lifecycle

var processor = serviceProvider.GetRequiredService<IEventProcessor>();

await processor.StartAsync();

// ... application runs ...

await processor.StopAsync();

EventEnvelope Properties

The EventEnvelope class contains metadata about each event:

Property Description
EventId Unique identifier for the event
EventType Full type name of the event
AssemblyQualifiedName Assembly-qualified type name
Event The serialized event bytes
RecordedAt When the event was recorded
Version Event version in the stream
CorrelationId Optional correlation ID
CompressionAlgorithm Compression algorithm used
EncryptionAlgorithm Encryption algorithm used
Headers Custom key-value headers

EventStream Properties

Property Description
Id The stream's unique identifier
Version Current stream version
CorrelationId Correlation ID (if set)
Removed Whether the stream has been removed
IsEmpty Whether the stream has no events
Count Total number of events

Exceptions

  • EventStreamConcurrencyException: Thrown when concurrent modification is detected
  • EventProcessingException: Thrown during projection event processing failures

Documentation

Please visit the Shuttle.Recall documentation for more information.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  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 (6)

Showing the top 5 NuGet packages that depend on Shuttle.Recall:

Package Downloads
Shuttle.Esb.Process

Shuttle.Esb process management using Shuttle.Recall event sourcing.

Shuttle.Recall.SqlServer.Storage

Sql Server event storage.

Shuttle.Recall.SqlServer.EventProcessing

Sql Server event projection processing.

Shuttle.Recall.Logging

Provides non-intrusive logging for Shuttle.Recall components.

Shuttle.Recall.OpenTelemetry

OpenTelemetry instrumentation for Shuttle.Recall.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
22.0.1 129 8/17/2026
22.0.0 161 8/17/2026
22.0.0-beta.1 152 8/15/2026
22.0.0-alpha.1 72 8/15/2026
21.0.3 804 4/17/2026
21.0.2 164 4/15/2026
21.0.2-rc3 196 4/11/2026
21.0.2-rc2 215 3/21/2026
21.0.2-rc1 136 3/21/2026
21.0.1-rc1 165 2/28/2026
21.0.1-beta 173 2/7/2026
21.0.0-alpha 158 1/18/2026
20.0.0 1,965 2/2/2025
18.0.0 608 8/5/2024
17.0.1 675 5/3/2024
17.0.0 623 4/30/2024
16.1.1 3,996 12/1/2022
16.0.0 4,200 9/4/2022
14.0.0 3,272 5/29/2022
13.1.0 2,952 5/6/2022
Loading failed