Skip to content

The search box knows all the secrets -- try it!

Fisher is part of the Critter Stack ecosystem.

JasperFx Logo JasperFx provides formal support for Fisher and other Critter Stack libraries. Please check our Support Plans for more details.

Bootstrapping Fisher

Fisher provides AddFisher() extension methods on IServiceCollection.

Basic Registration

cs
builder.Services.AddFisher(options =>
{
    options.Connection("Data Source=app.db");
});

Registration Overloads

cs
// Connection string only
builder.Services.AddFisher("Data Source=app.db");

// Action-based configuration
builder.Services.AddFisher(options =>
{
    options.Connection("Data Source=app.db");
    options.DatabaseSchemaName = "reporting";
});

// Factory-based, with access to the IServiceProvider
builder.Services.AddFisher(sp =>
{
    var config = sp.GetRequiredService<IConfiguration>();
    var opts = new StoreOptions();
    opts.Connection(config.GetConnectionString("Fisher")!);
    return opts;
});

Registered Services

ServiceLifetimeDescription
IDocumentStoreSingletonMain entry point; creates sessions
DocumentStoreSingletonThe same instance, for code that resolves the concrete type
ISessionFactorySingletonDecides which session flavor scoped resolution gets
IDocumentSessionScopedRead/write session with a unit of work
IQuerySessionScopedRead session

TIP

Everything Fisher hands the container implements IDisposable and IAsyncDisposable. That is not politeness: a ServiceProvider disposed synchronously refuses outright to dispose a service offering only IAsyncDisposable, which would make a scoped session unusable rather than merely less efficient.

Startup Options

AddFisher() returns a FisherConfigurationExpression carrying the host opt-ins:

cs
builder.Services.AddFisher(options =>
{
    options.Connection("Data Source=app.db");
    options.InitialData.Add(new SeedReferenceData());
    options.Projections.Snapshot<Order>(SnapshotLifecycle.Async);
})
.ApplyAllDatabaseChangesOnStartup()
.SeedInitialDataOnStartup()
.AddAsyncDaemon(DaemonMode.Solo);

ApplyAllDatabaseChangesOnStartup

Runs the Weasel migration once at startup so every table exists before the first session opens.

WARNING

AutoCreate.None wins over this registration. The hosted service starts and does nothing, rather than the registration quietly overriding your schema policy.

SeedInitialDataOnStartup

Runs every registered IInitialData. See Initial Baseline Data.

WARNING

This refuses to be registered before ApplyAllDatabaseChangesOnStartup(). Hosted services start in registration order, so the other way round writes to tables that do not exist yet — and that presents as no such table, which names the table and not the mistake.

AddAsyncDaemon

Hosts the async projection daemon.

cs
.AddAsyncDaemon(DaemonMode.Solo)
ModeBehaviour
SoloStarts the daemon in this process.
DisabledRegisters nothing.
ExternallyManagedRegisters nothing; you build and run the daemon yourself.
HotColdRefused. See below.

DANGER

DaemonMode.HotCold throws. Hot-cold failover means several nodes competing for a leadership lease through the database, and a Fisher store is a file that SQLite does not make safe to share across nodes. Accepting the mode and running Solo would give an application the opposite of the guarantee it asked for — every node projecting at once.

The daemon hosted service also logs the WAL warning at startup, which is the only place an operator would otherwise see it.

IConfigureFisher

Implement IConfigureFisher to modularise configuration — useful when a library contributes its own document types or projections:

cs
public class ReportingConfiguration : IConfigureFisher
{
    public void Configure(IServiceProvider services, StoreOptions options)
    {
        options.Schema.For<Report>().Duplicate(x => x.RunAt);
        options.Projections.Snapshot<Report>(SnapshotLifecycle.Async);
    }
}

Register it before AddFisher():

cs
builder.Services.AddSingleton<IConfigureFisher, ReportingConfiguration>();
builder.Services.AddFisher(options => options.Connection("Data Source=app.db"));

An untargeted IConfigureFisher reaches the primary store only. To contribute to a secondary store, use the targeted IConfigureFisher<T> — see Multiple Stores.

ConfigureFisher(...)

For a contribution that does not warrant a class of its own, ConfigureFisher(...) is the lambda form of the same seam — and the same surface Marten's ConfigureMarten and Polecat's ConfigurePolecat present, so integration code that layers its own options onto a store somebody else registered reads alike across the three stores:

cs
// Layered onto whatever store the application configured, either side of the AddFisher call.
services.ConfigureFisher(options =>
{
    options.Schema.For<Report>().Duplicate(x => x.RunAt);
    options.Projections.Snapshot<Report>(SnapshotLifecycle.Async);
});

// The overload taking the container as well, for configuration that needs a resolved service.
services.ConfigureFisher((serviceProvider, options) =>
{
    options.Projections.Add(
        serviceProvider.GetRequiredService<SalesProjection>(), ProjectionLifecycle.Async);
});

snippet source | anchor

Contributions run after the AddFisher(...) lambda, in registration order, and may be registered either side of it — they are resolved when the store is built, not when the call is made.

Session Factories

By default, the scoped IDocumentSession is a lightweight session. Supply your own ISessionFactory to change that — for instance to give every request a dirty-tracked session, or to stamp the current user onto the unit of work:

cs
public class UserSessionFactory : ISessionFactory
{
    private readonly IDocumentStore _store;
    private readonly IHttpContextAccessor _http;

    public UserSessionFactory(IDocumentStore store, IHttpContextAccessor http)
    {
        _store = store;
        _http = http;
    }

    public IQuerySession QuerySession() => _store.QuerySession();

    public IDocumentSession OpenSession()
    {
        var session = _store.DirtyTrackedSession();
        session.CurrentUserName = _http.HttpContext?.User.Identity?.Name;
        return session;
    }
}
cs
builder.Services.AddSingleton<ISessionFactory, UserSessionFactory>();

Released under the MIT License.