### Start async daemon with DailyShipmentsProjection Source: https://martendb.io/llms-full.txt Host setup that configures Marten with the async projection and starts the async daemon in Solo mode. The daemon must run continuously for async projections to process events. ```csharp await Host.CreateDefaultBuilder() .ConfigureServices(services => { services.AddMarten(opts => { opts.Connection(connectionString!); opts.AutoCreateSchemaObjects = AutoCreate.All; // Dev mode: create tables if missing opts.Projections.Add(ProjectionLifecycle.Async); }) // Turn on the async daemon in "Solo" mode // there are other modes, but this is the simplest .AddAsyncDaemon(DaemonMode.Solo); }) .StartAsync(cancellationToken); ``` -------------------------------- ### Install Marten.NodaTime NuGet package Source: https://martendb.io/llms-full.txt Installs the Marten.NodaTime NuGet package, which provides the necessary setup for Noda Time support in Marten. ```powershell PM> Install-Package Marten.NodaTime ``` -------------------------------- ### Start and append events to a stream Source: https://martendb.io/llms-full.txt Configures a Marten document store and demonstrates how to start a new event stream and append events to it. Uses a lightweight session and saves changes asynchronously. ```csharp var store = DocumentStore.For(_ => { _.Connection(ConnectionSource.ConnectionString); }); var questId = Guid.NewGuid(); await using var session = store.LightweightSession(); var started = new QuestStarted(questId, "Destroy the One Ring"); var joined1 = new MembersJoined(questId,1, "Hobbiton", ["Frodo", "Sam"]); // Start a brand new stream and commit the new events as // part of a transaction session.Events.StartStream(questId, started, joined1); // Append more events to the same stream var joined2 = new MembersJoined(questId,3, "Buckland", ["Merry", "Pippen"]); var joined3 = new MembersJoined(questId,10, "Bree", ["Aragorn"]); var arrived = new ArrivedAtLocation(questId, 15, "Rivendell"); session.Events.Append(questId, joined2, joined3, arrived); // Save the pending changes to db await session.SaveChangesAsync(); ``` -------------------------------- ### Add tenant databases and verify async daemon projections Source: https://martendb.io/configuration/multitenancy.html Integration test that adds three new tenant databases via MasterTableTenancy.AddDatabaseRecordAsync and verifies the async daemon starts projections for each. Uses IProjectionCoordinator to get per-database daemons and WaitForShardToBeRunning to confirm the "TripCustomName:All" shard is running. ```csharp [Fact] public async Task add_tenant_database_and_verify_the_daemon_projections_are_running() { // In this code block, I'm adding new tenant databases to the system that I // would expect Marten to discover and start up an asynchronous projection // daemon for all three newly discovered databases var tenancy = (MasterTableTenancy)theStore.Options.Tenancy; await tenancy.AddDatabaseRecordAsync("tenant1", tenant1ConnectionString); await tenancy.AddDatabaseRecordAsync("tenant2", tenant2ConnectionString); await tenancy.AddDatabaseRecordAsync("tenant3", tenant3ConnectionString); // This is a new service in Marten specifically to help you interrogate or // manipulate the state of running asynchronous projections within the current process var coordinator = _host.Services.GetRequiredService(); var daemon1 = await coordinator.DaemonForDatabase("tenant1"); var daemon2 = await coordinator.DaemonForDatabase("tenant2"); var daemon3 = await coordinator.DaemonForDatabase("tenant3"); // Just proving that the configured projections for the 3 new databases // are indeed spun up and running after Marten's new daemon coordinator // "finds" the new databases await daemon1.WaitForShardToBeRunning("TripCustomName:All", 30.Seconds()); await daemon2.WaitForShardToBeRunning("TripCustomName:All", 30.Seconds()); await daemon3.WaitForShardToBeRunning("TripCustomName:All", 30.Seconds()); } ``` -------------------------------- ### Custom IConfigureMarten implementation and registration Source: https://martendb.io/configuration/hostbuilder Shows an alternative to ConfigureMarten() by implementing a custom IConfigureMarten class and registering it in the IoC container. The registration example also shows how to register IConfigureMarten for multiple databases per Host, using IConfigureMarten as an example. ```csharp internal class UserMartenConfiguration: IConfigureMarten { public void Configure(IServiceProvider services, StoreOptions options) { options.RegisterDocumentType(); // and any other additional Marten configuration } } ``` ```csharp public static IServiceCollection AddUserModule2(this IServiceCollection services) { // This applies additional configuration to the main Marten DocumentStore // that is configured elsewhere services.AddSingleton(); // If you're using multiple databases per Host, register `IConfigureMarten`, like this: services.AddSingleton, InvoicingStoreConfiguration>(); // Other service registrations specific to the User submodule // within the bigger system return services; } ``` -------------------------------- ### Set up initial event stream with QuestStarted, MembersJoined, and MonsterSlayed Source: https://martendb.io/llms-full.txt Sets up the initial event stream with a QuestStarted, MembersJoined, and two MonsterSlayed events. Uses theStore.LightweightSession() to start a stream and save changes asynchronously. ```cs var started = new QuestStarted { Name = "Find the Orb" }; var joined = new MembersJoined { Day = 2, Location = "Faldor's Farm", Members = new[] { "Garion", "Polgara", "Belgarath" } }; var slayed1 = new MonsterSlayed { Name = "Troll" }; var slayed2 = new MonsterSlayed { Name = "Dragon" }; using (var session = theStore.LightweightSession()) { session.Events.StartStream(started.Name,started, joined, slayed1, slayed2); await session.SaveChangesAsync(); } ``` -------------------------------- ### GET /orders/{id:guid} Source: https://martendb.io/documents/aspnetcore StreamAggregate returns the latest projected aggregate state. ```APIDOC ## [GET] /orders/{id:guid} ### Description Returns the latest projected aggregate state for an event-sourced stream. Returns 404 if no stream exists. ### Method GET ### Endpoint /orders/{id:guid} ### Parameters #### Path Parameters - **id** (guid) - Required - The stream ID. ### Request Example No request body required. ### Response #### Success Response (200) - **body** (application/json) - The aggregate JSON. #### Response Example { "id": "...", "status": "..." } #### Error Response (404) - **body** (application/json) - Not found. ``` -------------------------------- ### GET /issues/{id:guid} Source: https://martendb.io/documents/aspnetcore StreamOne returns a single document with 404 on miss. ```APIDOC ## [GET] /issues/{id:guid} ### Description Returns a single document of type T. Returns 404 if no document matches the query. ### Method GET ### Endpoint /issues/{id:guid} ### Parameters #### Path Parameters - **id** (guid) - Required - The document ID. ### Request Example No request body required. ### Response #### Success Response (200) - **body** (application/json) - The document JSON. #### Response Example { "id": "...", "description": "..." } #### Error Response (404) - **body** (application/json) - Not found. ``` -------------------------------- ### Create databases for tenants on startup Source: https://martendb.io/llms-full.txt Configures Marten to create databases for tenants on startup. Requires bootstrapping via IHost; databases are checked on initialization and created on invalid catalog error (3D000). Requires CREATEDB privilege. May need Persist Security Info Property=true in connection string. ```csharp var maintenanceConnectionString = ConnectionSource.ConnectionString; var applicationConnectionString = ""; var builder = Host.CreateApplicationBuilder(); builder.Services.AddMarten(options => { // This might be different than the maintenance connection string options.Connection(applicationConnectionString); options.CreateDatabasesForTenants(c => { // Specify a db to which to connect in case database needs to be created. // If not specified, defaults to 'postgres' on the connection for a tenant. c.MaintenanceDatabase(maintenanceConnectionString); c.ForTenant() .CheckAgainstPgDatabase() .WithOwner("postgres") .WithEncoding("UTF-8") .ConnectionLimit(-1); }); }); using var host = builder.Build(); // NOTE: The new database will only be built upon the call to IHost.StartAsync() await host.StartAsync(); ``` -------------------------------- ### Sample EXPLAIN plan console output Source: https://martendb.io/diagnostics.html Example console output from the ExplainAsync() code, showing a sequential scan plan for the mt_doc_trade table. ```bash NodeType: Seq Scan RelationName: mt_doc_trade Alias: d StartupCost: 0 TotalCost: 24.13 PlanRows: 377 PlanWidth: 36 ``` -------------------------------- ### List available commands with dotnet run -- help Source: https://martendb.io/configuration/cli.html Run dotnet run -- help to list all available commands. The output shows the command aliases and descriptions, including db-apply, db-patch, projections, and marten. ```bash dotnet run -- help ``` ```bash The available commands are: Alias Description ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ check-env Execute all environment checks against the application codegen Utilities for working with JasperFx.CodeGeneration and JasperFx.RuntimeCompiler db-apply Applies all outstanding changes to the database(s) based on the current configuration db-assert Assert that the existing database(s) matches the current configuration db-dump Dumps the entire DDL for the configured Marten database db-list List all database(s) based on the current configuration db-patch Evaluates the current configuration against the database and writes a patch and drop file if there are any differences describe Writes out a description of your running application to either the console or a file help List all the available commands marten Advanced Marten operations to 'heal' event store projection issues or reset data projections Asynchronous projection and projection rebuilds resources Check, setup, or teardown stateful resources of this system run Start and run this .Net application storage Administer the Wolverine message storage ``` -------------------------------- ### ProjectLatest example with Report aggregate Source: https://martendb.io/llms-full.txt Complete example of using ProjectLatest with a custom aggregate. Defines events and the Report aggregate, starts a stream with pending events, projects locally without saving, and then saves later. The inline document is already queued for storage. ```csharp public record ReportCreated(string Title); public record SectionAdded(string SectionName); public record ReportPublished; public class Report { public Guid Id { get; set; } public string Title { get; set; } = ""; public int SectionCount { get; set; } public bool IsPublished { get; set; } public static Report Create(ReportCreated e) => new Report { Title = e.Title }; public void Apply(SectionAdded e) => SectionCount++; public void Apply(ReportPublished e) => IsPublished = true; } // In a command handler: await using var session = store.LightweightSession(); session.Events.StartStream(streamId, new ReportCreated("Q1 Report"), new SectionAdded("Revenue"), new SectionAdded("Costs") ); // Get the projected state WITHOUT saving first var report = await session.Events.ProjectLatest(streamId); // report.Title == "Q1 Report" // report.SectionCount == 2 // report.IsPublished == false // Save happens later — the inline document is already queued for storage await session.SaveChangesAsync(); ``` -------------------------------- ### List available commands Source: https://martendb.io/llms-full.txt Run the help command to list all available commands. ```bash dotnet run -- help ``` -------------------------------- ### GET /orders/{id:guid} Source: https://martendb.io/documents/aspnetcore Streams an aggregate with ETag support. The ETag is derived from the event stream's version and conditional requests are honored. ```APIDOC ## [GET] /orders/{id:guid} ### Description Streams an aggregate of type `Order` with ETag support. The ETag is derived from the event stream's version and conditional requests are honored. ### Method GET ### Endpoint /orders/{id:guid} ### Parameters #### Path Parameters - **id** (guid) - Required - The identifier of the order stream. ### Response #### Success Response (200) - **body** (Order) - The streamed aggregate. - **ETag** (string) - The ETag header derived from the stream version. #### Response Example ``` HTTP/1.1 200 OK ETag: "42" ``` #### Conditional Request If the client sends an `If-None-Match` header matching the current ETag, the server responds with `304 Not Modified` and an empty body. #### Request Example ```http GET /orders/f47ac10b-58cc-4372-a567-0e02b2c3d479 HTTP/1.1 If-None-Match: "42" ``` #### Response Example (304) ```http HTTP/1.1 304 Not Modified ETag: "42" ``` ``` -------------------------------- ### Compose satellite configurations in main host with AddMarten Source: https://martendb.io/configuration/composite-configuration Main host composition example: registers each satellite's IConfigureMarten/IAsyncConfigureMarten as singletons and calls AddMarten with only shared infrastructure (connection string, schema name). The satellites' implementations are wired into DI and compose the single DocumentStore. ```csharp var builder = Host.CreateApplicationBuilder(); // Each satellite's IConfigureMarten / IAsyncConfigureMarten gets wired into DI. builder.Services.AddSingleton(); // SatelliteA builder.Services.AddSingleton(); // SatelliteB // Main host carries only shared infrastructure. builder.Services.AddMarten(opts => { opts.Connection(connectionString); opts.DatabaseSchemaName = "modular_monolith"; }); using var host = builder.Build(); await host.StartAsync(); ``` -------------------------------- ### GET /issues/{id:guid} Source: https://martendb.io/documents/aspnetcore Streams a single document with ETag support. The ETag is derived from the document's version and conditional requests are honored. ```APIDOC ## [GET] /issues/{id:guid} ### Description Streams a single document of type `Issue` with ETag support. The ETag is derived from the document's `mt_version` and conditional requests are honored. ### Method GET ### Endpoint /issues/{id:guid} ### Parameters #### Path Parameters - **id** (guid) - Required - The identifier of the issue document. ### Response #### Success Response (200) - **body** (Issue) - The streamed document. - **ETag** (string) - The ETag header derived from the document's version. #### Response Example ``` HTTP/1.1 200 OK ETag: "3f2504e0-4f89-11d3-9a0c-0305e82c3301" ``` #### Conditional Request If the client sends an `If-None-Match` header matching the current ETag, the server responds with `304 Not Modified` and an empty body. #### Request Example ```http GET /issues/f47ac10b-58cc-4372-a567-0e02b2c3d479 HTTP/1.1 If-None-Match: "3f2504e0-4f89-11d3-9a0c-0305e82c3301" ``` #### Response Example (304) ```http HTTP/1.1 304 Not Modified ETag: "3f2504e0-4f89-11d3-9a0c-0305e82c3301" ``` ``` -------------------------------- ### DocumentStore.For implementation Source: https://martendb.io/configuration/storeoptions.html Shows the implementation of the static DocumentStore.For method, which builds a StoreOptions instance, applies the provided configuration action, and returns a new DocumentStore. This is the underlying mechanism for configuration methods like DocumentStore.For(configuration) and IServiceCollection.AddMarten(configuration). ```csharp public static DocumentStore For(Action configure) { var options = new StoreOptions(); configure(options); return new DocumentStore(options); } ``` -------------------------------- ### GET /minimal/order/{id:guid}/state Source: https://martendb.io/documents/aspnetcore Retrieves the metadata of a single event stream, including version, timestamps, and archived flag. Returns 404 if the stream does not exist. ```APIDOC ## GET /minimal/order/{id:guid}/state ### Description Writes the high level metadata of a single event stream as JSON, or returns 404 when the stream does not exist. ### Method GET ### Endpoint /minimal/order/{id:guid}/state ### Parameters #### Path Parameters - **id** (guid) - Required - The stream ID. ### Request Example No request body required. ### Response #### Success Response (200) - **id** (string) - The stream ID. - **key** (string|null) - The stream key, if string-keyed. - **version** (integer) - The current version of the stream. - **aggregateTypeName** (string) - The simple name of the aggregate type. - **lastTimestamp** (string) - The timestamp of the last event. - **created** (string) - The creation timestamp of the stream. - **isArchived** (boolean) - Whether the stream is archived. #### Response Example { "id": "0198e1b4-5b1c-7a1e-9a3f-2f2f5b6c7d8e", "key": null, "version": 2, "aggregateTypeName": "Order", "lastTimestamp": "2026-07-26T09:41:02.113Z", "created": "2026-07-26T09:41:02.098Z", "isArchived": false } #### Error Response (404) Returned when the stream does not exist. ``` -------------------------------- ### Implement IQueryPlan with QueryListPlan and longhand version Source: https://martendb.io/llms-full.txt Shows two ways to implement a query plan: a shorthand using the QueryListPlan base class and a longhand version implementing IQueryPlan and IBatchQueryPlan. The shorthand overrides Query(IQuerySession) to return an IQueryable; the longhand provides Fetch methods for both direct session queries and batched queries. Both filter by Color and order by Number. ```csharp public class ColorTargets: QueryListPlan { public Colors Color { get; } public ColorTargets(Colors color) { Color = color; } // All we're doing here is just turning around and querying against the session // All the same though, this approach lets you do much more runtime logic // than a compiled query can public override IQueryable Query(IQuerySession session) { return session.Query().Where(x => x.Color == Color).OrderBy(x => x.Number); } } // The above is short hand for: public class LonghandColorTargets: IQueryPlan>, IBatchQueryPlan> { public Colors Color { get; } public LonghandColorTargets(Colors color) { Color = color; } public Task> Fetch(IQuerySession session, CancellationToken token) { return session .Query() .Where(x => x.Color == Color) .OrderBy(x => x.Number) .ToListAsync(token: token); } public Task> Fetch(IBatchedQuery batch) { return batch .Query() .Where(x => x.Color == Color) .OrderBy(x => x.Number) .ToList(); } } ``` -------------------------------- ### GET /issues/{id:guid} Source: https://martendb.io/documents/aspnetcore Streams a single document using a Marten compiled query. The endpoint uses `StreamOne` with an `IssueById` compiled query to fetch an issue by its ID. ```APIDOC ## [GET] /issues/{id:guid} ### Description Streams a single document using a Marten compiled query. The endpoint uses `StreamOne` with an `IssueById` compiled query to fetch an issue by its ID. ### Method GET ### Endpoint /issues/{id:guid} ### Parameters #### Path Parameters - **id** (Guid) - Required - The ID of the issue to retrieve. ### Response #### Success Response (200) - Returns the issue document of type `TOut` (the compiled query's declared return type). #### Error Response (404) - Returned when the issue is not found. ### Code Example ```csharp app.MapGet("/issues/{id:guid}", (Guid id, IQuerySession session) => new StreamOne(session, new IssueById { Id = id })); ``` ``` -------------------------------- ### GET /minimal/order/{id:guid}/events/from/{fromVersion:long} Source: https://martendb.io/documents/aspnetcore Retrieves events from a specific version onward. Returns 200 instead of 404 when running off the end of a stream, which is expected when paging forward. ```APIDOC ## GET /minimal/order/{id:guid}/events/from/{fromVersion:long} ### Description Retrieves events from a specific version onward. Returns 200 instead of 404 when running off the end of a stream, which is expected when paging forward. ### Method GET ### Endpoint /minimal/order/{id:guid}/events/from/{fromVersion:long} ### Parameters #### Path Parameters - **id** (guid) - Required - The stream ID. - **fromVersion** (long) - Required - The version from which to start retrieving events. ### Request Example No request body required. ### Response #### Success Response (200) Returns a JSON array of EventResponse objects, same as the base /events endpoint. #### Response Example [ { "id": "0198e1b4-5b1c-7a1e-9a3f-2f2f5b6c7d8e", "version": 1, "sequence": 41, "streamId": "0198e1b4-5b1c-7a1e-9a3f-2f2f5b6c7d8e", "streamKey": null, "eventTypeName": "order_placed", "timestamp": "2026-07-26T09:41:02.098Z", "tenantId": "*DEFAULT*", "isArchived": false, "causationId": null, "correlationId": null, "headers": null, "data": { "description": "Widget", "amount": 99.95 } } ] ``` -------------------------------- ### Configure Static Tenant Database Mapping with MultiTenantedDatabases Source: https://martendb.io/configuration/multitenancy.html Set up static tenant-to-database mapping using MultiTenantedDatabases. This assumes databases are pre-created and no automatic provisioning occurs. ApplyAllDatabaseChangesOnStartup applies schema changes to all configured tenant databases on startup. ```csharp _host = await Host.CreateDefaultBuilder() .ConfigureServices(services => { services.AddMarten(opts => { // Explicitly map tenant ids to database connection strings opts.MultiTenantedDatabases(x => { // Map multiple tenant ids to a single named database x.AddMultipleTenantDatabase(db1ConnectionString, "database1") .ForTenants("tenant1", "tenant2"); // Map a single tenant id to a database, which uses the tenant id as well for the database identifier x.AddSingleTenantDatabase(tenant3ConnectionString, "tenant3"); x.AddSingleTenantDatabase(tenant4ConnectionString, "tenant4"); }); opts.RegisterDocumentType(); opts.RegisterDocumentType(); }) // All detected changes will be applied to all // the configured tenant databases on startup .ApplyAllDatabaseChangesOnStartup(); }).StartAsync(); ``` -------------------------------- ### Add database to pool and mark full Source: https://martendb.io/configuration/multitenancy.html Shows how to add a new database to the pool at runtime and mark an existing database as full. Marking a database as full prevents new tenant assignments to it while existing tenants continue to work normally. ```csharp // Add a new database to the pool at runtime await store.Advanced.AddDatabaseToPoolAsync("shard_05", newConnectionString, ct); // Mark a database as full — no new tenants will be assigned to it await store.Advanced.MarkDatabaseFullAsync("shard_01", ct); ``` -------------------------------- ### User reference document class Source: https://martendb.io/llms-full.txt Defines a User document type used as reference data in the event enrichment example. Includes a computed FullName property and a default constructor that generates a new Guid for Id. ```csharp public class User { public User() { Id = Guid.NewGuid(); } public List Friends { get; set; } public string[] Roles { get; set; } public Guid Id { get; set; } public string UserName { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public string FullName => $"{FirstName} {LastName}"; } ``` -------------------------------- ### Fetch and display PostgreSQL EXPLAIN plan with ExplainAsync() Source: https://martendb.io/diagnostics.html Use the ExplainAsync() extension method on IQueryable to fetch the PostgreSQL EXPLAIN plan for a Linq query, then print its properties. ```csharp // Explain() is an extension method off of IQueryable var plan = await queryable.ExplainAsync(); Console.WriteLine($"NodeType: {plan.NodeType}"); Console.WriteLine($"RelationName: {plan.RelationName}"); Console.WriteLine($"Alias: {plan.Alias}"); Console.WriteLine($"StartupCost: {plan.StartupCost}"); Console.WriteLine($"TotalCost: {plan.TotalCost}"); Console.WriteLine($"PlanRows: {plan.PlanRows}"); Console.WriteLine($"PlanWidth: {plan.PlanWidth}"); ``` -------------------------------- ### GET /minimal/order/{id:guid}/events Source: https://martendb.io/documents/aspnetcore Retrieves the raw events of a single event stream as a JSON array. Supports optional filters for version, timestamp, and fromVersion. Returns 404 by default when the stream is empty or does not exist. ```APIDOC ## GET /minimal/order/{id:guid}/events ### Description Writes the raw events of a single event stream as a JSON array. Supports optional filters for version, timestamp, and fromVersion. ### Method GET ### Endpoint /minimal/order/{id:guid}/events ### Parameters #### Path Parameters - **id** (guid) - Required - The stream ID. #### Query Parameters - **version** (long) - Optional - Filter events by version. - **timestamp** (DateTimeOffset) - Optional - Filter events by timestamp. - **fromVersion** (long) - Optional - Filter events from a specific version onward. ### Request Example No request body required. ### Response #### Success Response (200) - **id** (string) - The event ID. - **version** (integer) - The event version. - **sequence** (integer) - The event sequence number. - **streamId** (string) - The stream ID. - **streamKey** (string|null) - The stream key, if string-keyed. - **eventTypeName** (string) - The stable event type alias. - **timestamp** (string) - The event timestamp. - **tenantId** (string) - The tenant ID. - **isArchived** (boolean) - Whether the event is archived. - **causationId** (string|null) - The causation ID. - **correlationId** (string|null) - The correlation ID. - **headers** (object|null) - Additional headers. - **data** (object) - The event data. #### Response Example [ { "id": "0198e1b4-5b1c-7a1e-9a3f-2f2f5b6c7d8e", "version": 1, "sequence": 41, "streamId": "0198e1b4-5b1c-7a1e-9a3f-2f2f5b6c7d8e", "streamKey": null, "eventTypeName": "order_placed", "timestamp": "2026-07-26T09:41:02.098Z", "tenantId": "*DEFAULT*", "isArchived": false, "causationId": null, "correlationId": null, "headers": null, "data": { "description": "Widget", "amount": 99.95 } } ] #### Error Response (404) Returned when the stream does not exist or when the filter excludes all events, unless OnEmptyStatus is set to 200. ``` -------------------------------- ### Apply custom tenancy model with opts.Tenancy Source: https://martendb.io/configuration/multitenancy.html Assigns a custom tenancy model to the store options. The example shows the assignment but omits the surrounding setup, so it is intended to be used within a configuration block where 'opts' is the store options instance. ```csharp // Apply custom tenancy model opts.Tenancy = new MySpecialTenancy(); ``` -------------------------------- ### Implement IQueryPlanning on a compiled query for paging Source: https://martendb.io/llms-full.txt Implements IQueryPlanning on a compiled query to provide unique parameter values for query planning. SetUniqueValuesForQueryPlanning() must assign distinct values to parameters (e.g., Page=3, PageSize=20, Type=Guid) so Marten can build a plan; PageSize must be positive or Take() has no effect. A public QueryStatistics member yields the total record count. ```csharp public class CompiledTimeline : ICompiledListQuery, IQueryPlanning { public int PageSize { get; set; } = 20; [MartenIgnore] public int Page { private get; set; } = 1; public int SkipCount => (Page - 1) * PageSize; public string Type { get; set; } public Expression, IEnumerable>> QueryIs() => query => query.Where(i => i.Event == Type).Skip(SkipCount).Take(PageSize); public void SetUniqueValuesForQueryPlanning() { Page = 3; // Setting Page to 3 forces the SkipCount and PageSize to be different values PageSize = 20; // This has to be a positive value, or the Take() operator has no effect Type = Guid.NewGuid().ToString(); } // And hey, if you have a public QueryStatistics member on your compiled // query class, you'll get the total number of records public QueryStatistics Statistics { get; } = new QueryStatistics(); } ``` -------------------------------- ### Start a new event stream with Guid identifiers using StartStream Source: https://martendb.io/llms-full.txt Shows all overloads of StartStream for Guid stream identifiers: with or without an aggregate type, with a user-defined stream id, and with no aggregate type. StartStream throws ExistingStreamIdCollisionException if the stream id already exists; use Append() or FetchForWriting() to add events to an existing stream. Under per-tenant event partitioning, a duplicate StartStream surfaces as EventStreamUnexpectedMaxEventIdException instead. ```csharp public async Task start_stream_with_guid_stream_identifiers(IDocumentSession session) { var joined = new MembersJoined { Members = new[] { "Rand", "Matt", "Perrin", "Thom" } }; var departed = new MembersDeparted { Members = new[] { "Thom" } }; // Let Marten assign a new Stream Id, and mark the stream with an aggregate type // 'Quest' var streamId1 = session.Events.StartStream(joined, departed).Id; // Or pass the aggregate type in without generics var streamId2 = session.Events.StartStream(typeof(Quest), joined, departed); // Or instead, you tell Marten what the stream id should be var userDefinedStreamId = Guid.NewGuid(); session.Events.StartStream(userDefinedStreamId, joined, departed); // Or pass the aggregate type in without generics session.Events.StartStream(typeof(Quest), userDefinedStreamId, joined, departed); // Or forget about the aggregate type whatsoever var streamId4 = session.Events.StartStream(joined, departed); // Or start with a known stream id and no aggregate type session.Events.StartStream(userDefinedStreamId, joined, departed); // And persist the new stream of course await session.SaveChangesAsync(); } ``` -------------------------------- ### ConfigureMarten() extension method and usage in Host bootstrapping Source: https://martendb.io/configuration/hostbuilder Shows how to use ConfigureMarten() inside an extension method to add Marten configuration from a separate module, and how to call that extension method during host bootstrapping after AddMarten(). The ConfigureMarten() call applies additional configuration to the main DocumentStore that is configured elsewhere. ```csharp public static IServiceCollection AddUserModule(this IServiceCollection services) { // This applies additional configuration to the main Marten DocumentStore // that is configured elsewhere services.ConfigureMarten(opts => { opts.RegisterDocumentType(); }); // Other service registrations specific to the User submodule // within the bigger system return services; } ``` ```csharp using var host = await Host.CreateDefaultBuilder() .ConfigureServices(services => { // The initial Marten configuration services.AddMarten("some connection string"); // Other core service registrations services.AddLogging(); // Add the User module services.AddUserModule(); }).StartAsync(); ``` -------------------------------- ### Compiled query for a single User document Source: https://martendb.io/llms-full.txt Example compiled query that returns a single User document. It implements ICompiledQuery and uses multiple Where clauses to filter by FirstName, Username, and LastName, then calls Single() to get the matching document. ```csharp public class FindUserByAllTheThings: ICompiledQuery { public string Username { get; set; } public string FirstName { get; set; } public string LastName { get; set; } public Expression, User>> QueryIs() { return query => query.Where(x => x.FirstName == FirstName && Username == x.UserName) .Where(x => x.LastName == LastName) .Single(); } } ``` -------------------------------- ### Batch fetch stream state and events with FetchStreamStatePlan and FetchStreamPlan Source: https://martendb.io/llms-full.txt Combines FetchStreamStatePlan and FetchStreamPlan in a single batched query to fetch stream state and raw events in one database round trip. Both plans implement IBatchQueryPlan and can be used with any other registered queries. ```csharp [Fact] public async Task use_both_plans_in_one_batch() { var streamId = theSession.Events.StartStream(new QuestStarted { Name = "Destroy the One Ring" }, new MembersJoined(1, "Hobbiton", "Frodo", "Sam")).Id; await theSession.SaveChangesAsync(); // Start a batch query var batch = theSession.CreateBatchQuery(); // Fetching the stream state and the raw events of the same stream // in one database round trip var stateFetcher = batch.QueryByPlan(new FetchStreamStatePlan(streamId)); var eventsFetcher = batch.QueryByPlan(new FetchStreamPlan(streamId)); // Execute the batch query await batch.Execute(); var state = await stateFetcher; var events = await eventsFetcher; state.ShouldNotBeNull(); state.Version.ShouldBe(2); events.Count.ShouldBe(2); } ``` -------------------------------- ### Configure Marten with inline Order snapshot Source: https://martendb.io/llms-full.txt Configures Marten to snapshot the Order aggregate inline and enables optimizations for inline aggregates and lightweight sessions. This setup is used in the archiving example to keep the Order aggregate updated in the same transaction as appended events. ```csharp var builder = Host.CreateApplicationBuilder(); builder.Services.AddMarten(opts => { opts.Connection("some connection string"); // The Order aggregate is updated Inline inside the // same transaction as the events being appended opts.Projections.Snapshot(SnapshotLifecycle.Inline); // Opt into an optimization for the inline aggregates // used with FetchForWriting() opts.Projections.UseIdentityMapForAggregates = true; }) // This is also a performance optimization in Marten to disable the // identity map tracking overall in Marten sessions if you don't // need that tracking at runtime .UseLightweightSessions(); ``` -------------------------------- ### Configure soft-deleted document partitioning with SoftDeletedWithPartitioning Source: https://martendb.io/documents/deletes Shows how to opt into PostgreSQL table partitioning for soft-deleted documents to improve query performance. Includes options for a single document type, adding an index on deletion timestamp, and applying partitioning to all soft-deleted documents. ```cs var store = DocumentStore.For(opts => { opts.Connection("some connection string"); // Opt into partitioning for one document type opts.Schema.For().SoftDeletedWithPartitioning(); // Opt into partitioning and an index on deletion timestamp for one document type opts.Schema.For().SoftDeletedWithPartitioningAndIndex(); // Opt into partitioning for all soft-deleted documents opts.Policies.AllDocumentsSoftDeletedWithPartitioning(); }); ``` -------------------------------- ### Open a snapshot session with LightweightSession and IsolationLevel.Snapshot Source: https://martendb.io/llms-full.txt Use this snippet to open a session with a snapshot transaction isolation level. It is an alternative to the serializable example and uses the synchronous LightweightSession method with the IsolationLevel.Snapshot argument. The session starts a snapshot transaction and loads the saga state. ```csharp public async Task execute_saga(IDocumentStore store, Guid sagaId, CancellationToken ct) { // The session below will open its connection and start a // snapshot transaction await using var session = store.LightweightSession(IsolationLevel.Snapshot); var state = await session.LoadAsync(sagaId, ct); // do some work against the saga await session.SaveChangesAsync(ct); } ```