### Start Async Daemon Source: https://github.com/jasperfx/marten/blob/master/docs/events/projections/async-daemon.md Starts the async daemon for the application in a console window. This is a basic command to get the daemon running. ```bash dotnet run -- projections ``` -------------------------------- ### Starting Event Streams with Various Identifiers Source: https://github.com/jasperfx/marten/blob/master/docs/events/appending.md Demonstrates different ways to start a new event stream using `session.Events.StartStream`. This includes letting Marten assign a GUID, providing a custom GUID, and optionally specifying the aggregate type or omitting it. ```cs 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(); } ``` -------------------------------- ### HStore Tag Query Examples Source: https://github.com/jasperfx/marten/blob/master/docs/events/dcb.md These SQL examples show how to query events based on tags when using the HStore storage mode. They utilize the PostgreSQL '@>' operator for efficient containment lookups. ```sql -- Single tag: e.tags @> hstore('student', 'STU-001') -- Two tags OR: e.tags @> hstore('student', 'STU-001') OR e.tags @> hstore('course', 'CS-101') ``` -------------------------------- ### Install Node.js Packages Source: https://github.com/jasperfx/marten/blob/master/CONTRIBUTING.md Run this command in the root directory after installing Node.js to install required packages for documentation updates. ```bash npm install ``` -------------------------------- ### Start Async Daemon Interactively Source: https://github.com/jasperfx/marten/blob/master/docs/events/projections/async-daemon.md Starts the async daemon and allows interactive selection of which projections to run. Use the '-i' or '--interactive' flags. ```bash dotnet run -- projections -i ``` ```bash dotnet run -- projections --interactive ``` -------------------------------- ### Configure Subscription Starting Position Source: https://github.com/jasperfx/marten/blob/master/docs/events/subscriptions.md Demonstrates various ways to set the initial event sequence number for a Marten subscription. Use `SubscribeFromPresent()` to start from the latest event, `SubscribeFromSequence(number)` to start from a specific event number, or `SubscribeFromTime(timestamp)` to start from a given date and time. These can also be applied to specific databases using a database name argument. ```csharp var builder = Host.CreateApplicationBuilder(); builder.Services.AddMarten(opts => { opts.Connection(builder.Configuration.GetConnectionString("marten")); }) // Marten also supports a Scoped lifecycle, and quietly forward Transient // to Scoped .AddSubscriptionWithServices(ServiceLifetime.Singleton, o => { // Start the subscription at the most current "high water mark" of the // event store. This effectively makes the subscription a "hot" // observable that only sees events when the subscription is active o.Options.SubscribeFromPresent(); // Only process events in the store from a specified event sequence number o.Options.SubscribeFromSequence(1000); // Only process events in the store by determining the floor by the event // timestamp information o.Options.SubscribeFromTime(new DateTimeOffset(2024, 4, 1, 0, 0, 0, 0.Seconds())); // All of these options can be explicitly applied to only a single // named database when using multi-tenancy through separate databases o.Options.SubscribeFromPresent("Database1"); o.Options.SubscribeFromSequence(2000, "Database2"); }) .AddAsyncDaemon(DaemonMode.HotCold); using var host = builder.Build(); await host.StartAsync(); ``` -------------------------------- ### Use Async Daemon Alone Source: https://github.com/jasperfx/marten/blob/master/docs/events/projections/async-daemon.md Demonstrates how to build, start, stop, and manage projection daemons and agents directly from an IDocumentStore. Useful for custom bootstrapping scenarios. ```csharp public static async Task UseAsyncDaemon(IDocumentStore store, CancellationToken cancellation) { using var daemon = await store.BuildProjectionDaemonAsync(); // Fire up everything! await daemon.StartAllAsync(); // or instead, rebuild a single projection await daemon.RebuildProjectionAsync("a projection name", 5.Minutes(), cancellation); // or a single projection by its type await daemon.RebuildProjectionAsync(5.Minutes(), cancellation); // Be careful with this. Wait until the async daemon has completely // caught up with the currently known high water mark await daemon.WaitForNonStaleData(5.Minutes()); // Start a single projection shard await daemon.StartAgentAsync("shard name", cancellation); // Or change your mind and stop the shard you just started await daemon.StopAgentAsync("shard name"); // No, shut them all down! await daemon.StopAllAsync(); } ``` -------------------------------- ### Example Usage of ProjectLatest Source: https://github.com/jasperfx/marten/blob/master/docs/events/projections/project-latest.md This example demonstrates how to use ProjectLatest in a command handler to get the projected state of a 'Report' aggregate, including pending events, before saving. ```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(); ``` -------------------------------- ### Configure Async Projection Daemon Source: https://github.com/jasperfx/marten/blob/master/docs/tutorials/cross-aggregate-views.md Setup for the background daemon required to process asynchronous projections. ```csharp var host = await Host.CreateDefaultBuilder() .ConfigureServices(services => { services.AddMarten(opts => { opts.Connection("host=localhost;database=postgres;username=postgres;password=postgres"); opts.Projections.Add(new DailyShipmentsProjection(), ProjectionLifecycle.Async); }) .AddAsyncDaemon(DaemonMode.Solo); }) .StartAsync(); ``` -------------------------------- ### Replacing Marten's Polly Configuration Source: https://github.com/jasperfx/marten/blob/master/docs/configuration/retries.md Demonstrates how to completely replace Marten's default Polly configuration by providing a custom setup within DocumentStore.For. ```csharp using var store = DocumentStore.For(opts => { opts.Connection("some connection string"); opts.ConfigurePolly(builder => { builder.AddRetry(new() { ShouldHandle = new PredicateBuilder().Handle().Handle(), MaxRetryAttempts = 10, // this is excessive, but just wanted to show something different Delay = TimeSpan.FromMilliseconds(50), BackoffType = DelayBackoffType.Linear }); }); }); ``` -------------------------------- ### Install Marten.PostGIS Package Source: https://github.com/jasperfx/marten/blob/master/docs/postgres/postgis.md Add the Marten.PostGIS NuGet package to your project using the .NET CLI. ```shell dotnet add package Marten.PostGIS ``` -------------------------------- ### Install Marten.EntityFrameworkCore NuGet Package Source: https://github.com/jasperfx/marten/blob/master/docs/events/projections/efcore.md Add the Marten.EntityFrameworkCore NuGet package to your project to enable EF Core projections. ```bash dotnet add package Marten.EntityFrameworkCore ``` -------------------------------- ### Start a Standalone Document Store Source: https://github.com/jasperfx/marten/blob/master/docs/getting-started.md Create a new Marten DocumentStore instance using a connection string. This is useful for scenarios outside of the generic host. ```csharp var store = DocumentStore .For("host=localhost;database=marten_testing;password=mypassword;username=someuser"); ``` -------------------------------- ### Enable a PostgreSQL Extension Source: https://github.com/jasperfx/marten/blob/master/docs/schema/extensions.md Enable a PostgreSQL extension using `Weasel.Postgresql.Extension`. This example enables the 'unaccent' extension, which is included with PostgreSQL. ```cs StoreOptions(opts => { opts.RegisterDocumentType(); // Unaccent is an extension ships with postgresql // and removes accents (diacritic signs) from strings var extension = new Extension("unaccent"); opts.Storage.ExtendedSchemaObjects.Add(extension); }); await theStore.Storage.ApplyAllConfiguredChangesToDatabaseAsync(); ``` -------------------------------- ### SequentialGuidIdentification Constructor Example Source: https://github.com/jasperfx/marten/blob/master/src/Marten/Internal/ClosedShape/README.md Demonstrates how to instantiate SequentialGuidIdentification for a document type, providing the MemberInfo for the ID property. ```csharp new SequentialGuidIdentification( typeof(Order).GetProperty(nameof(Order.Id))!); ``` -------------------------------- ### Custom User Marten Configuration Implementation Source: https://github.com/jasperfx/marten/blob/master/docs/configuration/hostbuilder.md An example of implementing the `IConfigureMarten` interface to provide custom Marten configuration, such as registering document types. ```csharp internal class UserMartenConfiguration: IConfigureMarten { public void Configure(IServiceProvider services, StoreOptions options) { options.RegisterDocumentType(); // and any other additional Marten configuration } } ``` -------------------------------- ### Registering Custom Projections Source: https://github.com/jasperfx/marten/blob/master/docs/events/projections/custom.md Demonstrates how to register custom projections with the Marten `DocumentStore`. This example shows registration for both inline and asynchronous projection lifecycles. ```cs var store = DocumentStore.For(opts => { opts.Connection("some connection string"); // Use inline lifecycle opts.Projections.Add(new QuestPatchTestProjection(), ProjectionLifecycle.Inline); // Or use this as an asychronous projection opts.Projections.Add(new QuestPatchTestProjection(), ProjectionLifecycle.Async); }); ``` -------------------------------- ### Create Table in PostgreSQL Source: https://github.com/jasperfx/marten/blob/master/docs/postgres/naming.md Example of creating a table in PostgreSQL using Pascal casing for table and column names. PostgreSQL will store these in lowercase. ```sql create table if not exists Product ( Id serial, Name text, Price money, IsDeleted bool, CategoryId int, CreatedByUser int, ModifiedByUser int ); ``` -------------------------------- ### Implement IQueryPlanning for Compiled Queries Source: https://github.com/jasperfx/marten/blob/master/docs/documents/querying/compiled-queries.md This example demonstrates how to implement the `IQueryPlanning` interface on a compiled query type to help Marten determine unique parameter values for query planning. The `SetUniqueValuesForQueryPlanning` method is crucial for this process. ```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(); } ``` -------------------------------- ### GitHub Actions build and test job setup Source: https://github.com/jasperfx/marten/blob/master/docs/devops/devops.md Configure a job to run on 'ubuntu-latest', including a PostgreSQL service for integration tests. This job checks out code, sets up .NET, restores dependencies, builds, and runs tests. ```yaml jobs: build-test: runs-on: ubuntu-latest services: postgres: image: postgres:16 env: POSTGRES_USER: user POSTGRES_PASSWORD: password options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 ports: - 5432:5432 steps: - name: Checkout uses: actions/checkout@v4 - name: Setup .NET uses: actions/setup-dotnet@v3 with: dotnet-version: ${{ env.DOTNET_VERSION }} - name: Dotnet restore run: dotnet restore ${{ env.SOLUTION }}.sln -verbosity:quiet - name: Build ${{ env.PROJECT }} run: dotnet build ${{ env.SOLUTION }}.sln -c ${{ env.BUILD_CONFIGURATION }} --no-restore - name: Test ${{ env.PROJECT }} run: dotnet test ${{ env.SOLUTION }}.sln -c ${{ env.BUILD_CONFIGURATION }} --no-restore --no-build --results-directory reports --logger "trx;" --nologo ``` -------------------------------- ### Get Distinct Anonymous Objects with Distinct() Source: https://github.com/jasperfx/marten/blob/master/docs/documents/querying/linq/operators.md Shows how to use Distinct() on an anonymous type projection to get unique combinations of Number and Decimal. This example is specific to the Newtonsoft serializer. ```cs theSession.Store(new Target {Number = 1, Decimal = 1.0M}); theSession.Store(new Target {Number = 1, Decimal = 2.0M}); theSession.Store(new Target {Number = 1, Decimal = 2.0M}); theSession.Store(new Target {Number = 2, Decimal = 1.0M}); theSession.Store(new Target {Number = 2, Decimal = 2.0M}); theSession.Store(new Target {Number = 2, Decimal = 1.0M}); await theSession.SaveChangesAsync(); var queryable = theSession.Query().Select(x => new { x.Number, x.Decimal }).Distinct(); (await queryable.ToListAsync()).Count.ShouldBe(4); ``` -------------------------------- ### HardDeletedStartAndStopProjection Example Source: https://github.com/jasperfx/marten/blob/master/docs/events/projections/explicit.md Demonstrates an explicit projection for managing a stream with potential hard deletions and restarts. It includes logic to conditionally apply events based on the aggregate's current state. ```csharp public partial class HardDeletedStartAndStopProjection: SingleStreamProjection { public HardDeletedStartAndStopProjection() { // This is an optional, but potentially important optimization // for the async daemon so that it sets up an allow list // of the event types that will be run through this projection IncludeType(); IncludeType(); IncludeType(); IncludeType(); } public override (HardDeletedStartAndStopAggregate?, ActionType) DetermineAction(HardDeletedStartAndStopAggregate? snapshot, Guid identity, IReadOnlyList events) { var actionType = ActionType.Store; if (snapshot == null && events.HasNoEventsOfType()) { return (snapshot, ActionType.Nothing); } var eventData = events.ToQueueOfEventData(); while (eventData.Any()) { var data = eventData.Dequeue(); switch (data) { case Start: snapshot = new HardDeletedStartAndStopAggregate { // Have to assign the identity ourselves Id = identity }; break; case Increment when snapshot is { }: // Use explicit code to only apply this event // if the snapshot already exists snapshot.Increment(); break; case End when snapshot is {}: actionType = ActionType.HardDelete; snapshot = null; break; case Restart when snapshot == null: // Got to "undo" the soft delete status actionType = ActionType.Store; snapshot = new HardDeletedStartAndStopAggregate { Id = identity }; break; } } return (snapshot, actionType); } } ``` -------------------------------- ### Add New Tenant Databases and Verify Projections Source: https://github.com/jasperfx/marten/blob/master/docs/configuration/multitenancy.md Demonstrates adding new tenant databases to the system and verifying that Marten's asynchronous projection daemon discovers and starts projections for them. This is useful for dynamically scaling tenant databases. ```csharp var tenancy = (MasterTableTenancy)theStore.Options.Tenancy; await tenancy.AddDatabaseRecordAsync("tenant1", tenant1ConnectionString); await tenancy.AddDatabaseRecordAsync("tenant2", tenant2ConnectionString); await tenancy.AddDatabaseRecordAsync("tenant3", tenant3ConnectionString); var coordinator = _host.Services.GetRequiredService(); var daemon1 = await coordinator.DaemonForDatabase("tenant1"); var daemon2 = await coordinator.DaemonForDatabase("tenant2"); var daemon3 = await coordinator.DaemonForDatabase("tenant3"); await daemon1.WaitForShardToBeRunning("TripCustomName:All", 30.Seconds()); await daemon2.WaitForShardToBeRunning("TripCustomName:All", 30.Seconds()); await daemon3.WaitForShardToBeRunning("TripCustomName:All", 30.Seconds()); ``` -------------------------------- ### Marten Document Identity Examples Source: https://github.com/jasperfx/marten/blob/master/docs/documents/identity.md Demonstrates various ways to define an identifier for a Marten document, including String, Guid, and Int types. ```csharp public class Division { // String property as Id public string Id { get; set; } } public class Category { // Guid's work, fields too public Guid Id; } public class Invoice { // int's and long's can be the Id // "id" is accepted public int id { get; set; } } ``` -------------------------------- ### TagTables Storage Schema Example Source: https://github.com/jasperfx/marten/blob/master/docs/events/dcb.md This SQL snippet shows the table structure created for a single tag type when using the default TagTables storage mode. Each tag type gets its own table with a composite primary key. ```sql CREATE TABLE IF NOT EXISTS mt_event_tag_student ( value uuid NOT NULL, seq_id bigint NOT NULL, CONSTRAINT pk_mt_event_tag_student PRIMARY KEY (value, seq_id), CONSTRAINT fk_mt_event_tag_student_events FOREIGN KEY (seq_id) REFERENCES mt_events(seq_id) ON DELETE CASCADE ); ``` -------------------------------- ### Define a Reusable Query Plan with IQueryPlan and IBatchQueryPlan Source: https://github.com/jasperfx/marten/blob/master/docs/documents/querying/compiled-queries.md This is the longhand version of defining a query plan, explicitly implementing `IQueryPlan>` and `IBatchQueryPlan>`. It demonstrates fetching data directly from `IQuerySession` and `IBatchedQuery`. ```cs 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(); } } ``` -------------------------------- ### Using ConfigureMarten in HostBuilder Source: https://github.com/jasperfx/marten/blob/master/docs/configuration/hostbuilder.md Demonstrates how to integrate custom module configurations, like `AddUserModule()`, into the application's bootstrapping process using `Host.CreateDefaultBuilder`. ```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(); ``` -------------------------------- ### Install Marten.AspNetCore Nuget Package Source: https://github.com/jasperfx/marten/blob/master/docs/documents/aspnetcore.md Install the Marten.AspNetCore package using the Package Manager Console. ```powershell PM> Install-Package Marten.AspNetCore ``` -------------------------------- ### Define entities with Guid and Matter identifiers Source: https://github.com/jasperfx/marten/blob/master/docs/scenarios/using-sequence-for-unique-id.md Define domain models that utilize both a system-internal Guid and a human-readable integer identifier. ```csharp public class Contract { public Guid Id { get; set; } public int Matter { get; set; } // Other fields... } public class Inquiry { public Guid Id { get; set; } public int Matter { get; set; } // Other fields... } ``` -------------------------------- ### Configure Global Sequential GUID Generation Policy Source: https://github.com/jasperfx/marten/blob/master/docs/documents/identity.md Apply the SequentialGuidIdGeneration strategy to all document types that use GUIDs by default using Policies.ForAllDocuments. ```csharp options.Policies.ForAllDocuments(m => { if (m.IdType == typeof(Guid)) { m.IdStrategy = new SequentialGuidIdGeneration(); } }); ``` -------------------------------- ### TripProjection with Create and Apply Methods Source: https://github.com/jasperfx/marten/blob/master/docs/events/projections/conventions.md Demonstrates a TripProjection using a Create() method for initial aggregate instantiation and Apply() methods for state changes. This pattern is used for asynchronous projections. ```csharp public partial class TripProjection: SingleStreamProjection { // These methods can be either public, internal, or private but there's // a small performance gain to making them public public void Apply(Arrival e, Trip trip) => trip.State = e.State; public void Apply(Travel e, Trip trip) { Debug.WriteLine($"Trip {trip.Id} Traveled " + e.TotalDistance()); trip.Traveled += e.TotalDistance(); Debug.WriteLine("New total distance is " + e.TotalDistance()); } public void Apply(TripEnded e, Trip trip) { trip.Active = false; trip.EndedOn = e.Day; } public Trip Create(IEvent started) { return new Trip { Id = started.StreamId, StartedOn = started.Data.Day, Active = true }; } public bool ShouldDelete(TripAborted _) => true; public bool ShouldDelete(Breakdown e) => e.IsCritical; public bool ShouldDelete(VacationOver _, Trip trip) => trip.Traveled > 1000; } ``` -------------------------------- ### Manual Document Session Operations Source: https://github.com/jasperfx/marten/blob/master/docs/documents/sessions.md Demonstrates creating a lightweight session, storing new and updated documents, deleting documents, and persisting changes. ```cs await using var session = store.LightweightSession(); var user = new User { FirstName = "Jeremy", LastName = "Miller" }; // Manually adding the new user to the session session.Store(user); var existing = session.Query().Single(x => x.FirstName == "Max"); existing.Internal = false; // Manually marking an existing user as changed session.Store(existing); // Marking another existing User document as deleted session.Delete(Guid.NewGuid()); // Persisting the changes to the database await session.SaveChangesAsync(); ``` -------------------------------- ### Custom GinIndexedAttribute Example Source: https://github.com/jasperfx/marten/blob/master/docs/configuration/storeoptions.md An example of a custom Marten attribute that adds a Gin index to the JSONB storage for a document. This attribute is applied at the class level. ```cs [AttributeUsage(AttributeTargets.Class)] public class GinIndexedAttribute: MartenAttribute { public override void Modify(DocumentMapping mapping) { mapping.AddGinIndexToData(); } } ``` -------------------------------- ### Initialize Marten Database Source: https://github.com/jasperfx/marten/blob/master/README.md Use this command to spin up a PostgreSQL database with the default Marten test configuration when no explicit connection string is set. ```bash docker-compose up ``` ```bash dotnet run --framework net6.0 -- init-db ``` -------------------------------- ### Implement IVersioned for Guid Versioning Source: https://github.com/jasperfx/marten/blob/master/docs/documents/concurrency.md Implement the IVersioned interface to enable optimistic concurrency checks with Guid versions. Marten automatically manages the Version property. ```csharp public class MyVersionedDoc: IVersioned { public Guid Id { get; set; } public Guid Version { get; set; } } ``` -------------------------------- ### Display Marten CLI Help Source: https://github.com/jasperfx/marten/blob/master/docs/configuration/cli.md Run this command to see the available Marten CLI operations and their descriptions. ```bash dotnet run -- help marten ``` -------------------------------- ### Compiled List Query Example (No Select) Source: https://github.com/jasperfx/marten/blob/master/docs/documents/querying/compiled-queries.md Example of a compiled query implementing ICompiledListQuery to fetch users by first name. It uses a simple Where clause and returns IEnumerable. ```csharp public class UsersByFirstName: ICompiledListQuery { public static int Count; public string FirstName { get; set; } public Expression, IEnumerable>> QueryIs() { return query => query.Where(x => x.FirstName == FirstName); } } ``` -------------------------------- ### Configure Marten DocumentStore Source: https://github.com/jasperfx/marten/blob/master/docs/tutorials/getting-started.md Initialize the DocumentStore with a connection string and schema auto-creation enabled. ```csharp <<< @/src/samples/FreightShipping/GettingStarted.cs#store-setup ``` -------------------------------- ### Example Compiled Query: FindByFirstName Source: https://github.com/jasperfx/marten/blob/master/docs/documents/querying/compiled-queries.md An example implementation of a compiled query to find a user document by their first name. It defines a public property for the filter criteria and implements the QueryIs method. ```csharp public class FindByFirstName: ICompiledQuery { public string FirstName { get; set; } public Expression, User>> QueryIs() { return q => q.FirstOrDefault(x => x.FirstName == FirstName); } } ``` -------------------------------- ### Configure Per-Tenant Unique Indexes Source: https://github.com/jasperfx/marten/blob/master/docs/documents/indexing/unique.md Demonstrates setting up a Marten DocumentStore with per-tenant unique indexes for User and Client entities. Use this to enforce uniqueness constraints within each tenant's data. ```csharp var store = DocumentStore.For(_ => { _.Connection(ConnectionSource.ConnectionString); _.DatabaseSchemaName = "unique_text"; // This creates a duplicated field unique index on firstname, lastname and tenant_id _.Schema.For().MultiTenanted().UniqueIndex(UniqueIndexType.DuplicatedField, "index_name", TenancyScope.PerTenant, x => x.FirstName, x => x.LastName); // This creates a computed unique index on client name and tenant_id _.Schema.For().MultiTenanted().UniqueIndex(UniqueIndexType.Computed, "index_name", TenancyScope.PerTenant, x => x.Name); }); ``` -------------------------------- ### Compiled List Query Example (With Select) Source: https://github.com/jasperfx/marten/blob/master/docs/documents/querying/compiled-queries.md Example of a compiled query implementing ICompiledListQuery to fetch user names by first name. It uses Where() and Select() to return only the UserName strings. ```csharp public class UserNamesForFirstName: ICompiledListQuery { public Expression, IEnumerable>> QueryIs() { return q => q .Where(x => x.FirstName == FirstName) .Select(x => x.UserName); } public string FirstName { get; set; } } ``` -------------------------------- ### ASP.NET Core Endpoints for User Operations Source: https://github.com/jasperfx/marten/blob/master/docs/getting-started.md Illustrates how to create API endpoints for creating, retrieving, and querying user documents using Marten's IDocumentSession and IQuerySession. These examples demonstrate injecting sessions and performing basic persistence and query operations. ```cs // You can inject the IDocumentStore and open sessions yourself app.MapPost("/user", async (CreateUserRequest create, // Inject a session for querying, loading, and updating documents [FromServices] IDocumentSession session) => { var user = new User { FirstName = create.FirstName, LastName = create.LastName, Internal = create.Internal }; session.Store(user); // Commit all outstanding changes in one // database transaction await session.SaveChangesAsync(); }); app.MapGet("/users", async (bool internalOnly, [FromServices] IDocumentSession session, CancellationToken ct) => { return await session.Query() .Where(x=> x.Internal == internalOnly) .ToListAsync(ct); }); // OR use the lightweight IQuerySession if all you're doing is running queries app.MapGet("/user/{id:guid}", async (Guid id, [FromServices] IQuerySession session, CancellationToken ct) => { return await session.LoadAsync(id, ct); }); ``` -------------------------------- ### Default Noda Time Setup in Marten Source: https://github.com/jasperfx/marten/blob/master/docs/documents/noda-time.md Configure the DocumentStore to use Noda Time for date and time handling. This setup also configures the default JSON serializer for Noda Time types. ```csharp var store = DocumentStore.For(_ => { _.Connection(ConnectionSource.ConnectionString); // sets up NodaTime handling _.UseNodaTime(); }); ``` -------------------------------- ### Preview Linq Query Explain Plan Source: https://github.com/jasperfx/marten/blob/master/docs/diagnostics.md Use the ExplainAsync() extension method on an IQueryable to fetch and preview the PostgreSQL EXPLAIN plan for a Linq query. This aids in diagnosing performance issues. ```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}"); ``` -------------------------------- ### Configure Indexes to Start with Tenant ID Source: https://github.com/jasperfx/marten/blob/master/docs/documents/multi-tenancy.md Ensures that database indexes, especially for conjoined multi-tenancy, start with tenant_id. This improves index selectivity and performance by allowing PostgreSQL to efficiently filter within partitions. ```csharp _.Policies.ForAllDocuments(x => { x.StartIndexesByTenantId = true; }); _.Schema.For() .StartIndexesByTenantId() .Index(x => x.UserName); ``` -------------------------------- ### Shipment Example with Inline Projection Source: https://github.com/jasperfx/marten/blob/master/docs/tutorials/event-sourced-aggregate.md Illustrates how an inline SingleStreamProjection works with the shipment example. Each time an event is appended, Marten applies changes and updates the document within the same transaction, ensuring strong consistency. ```csharp public class EventSourcedAggregate { public static void Configure(StoreOptions options) { options.Projections.Add(ProjectionLifecycle.Inline); } public static async Task RunShipmentExample(IDocumentStore store) { using var session = store.LightweightSession(); // Create a new shipment var shipmentId = Guid.NewGuid(); session.Append(shipmentId, new ShipmentCreated(DateTimeOffset.UtcNow, "Road", "123 Main St", "456 Secondary St")); await session.SaveChangesAsync(); // Change the destination await session.Events.Append(shipmentId, new ShipmentDestinationChanged(DateTimeOffset.UtcNow, "789 Other St")); await session.SaveChangesAsync(); // Mark as shipped await session.Events.Append(shipmentId, new ShipmentShipped(DateTimeOffset.UtcNow)); await session.SaveChangesAsync(); // Fetch the projected document var shipment = await session.LoadAsync(shipmentId); // Assertions would go here } } ``` -------------------------------- ### Using Batched Queries in Marten Source: https://github.com/jasperfx/marten/blob/master/docs/documents/querying/batched-queries.md Demonstrates how to create and execute a batch query, including loading documents by ID, querying with LINQ, and using selectors like `Any()`, `Count()`, and `First()`. ```cs var batch = session.CreateBatchQuery(); // Fetch a single document by its Id var user1 = batch.Load("username"); // Fetch multiple documents by their id's var admins = batch.LoadMany().ById("user2", "user3"); // User-supplied sql var toms = batch.Query("where first_name == ?", "Tom"); // Where with Linq var jills = batch.Query().Where(x => x.FirstName == "Jill").ToList(); // Any() queries var anyBills = batch.Query().Any(x => x.FirstName == "Bill"); // Count() queries var countJims = batch.Query().Count(x => x.FirstName == "Jim"); // The Batch querying supports First/FirstOrDefault/Single/SingleOrDefault() selectors: var firstInternal = batch.Query().OrderBy(x => x.LastName).First(x => x.Internal); // Kick off the batch query await batch.Execute(); // All of the query mechanisms of the BatchQuery return // Task's that are completed by the Execute() method above var internalUser = await firstInternal; Debug.WriteLine($"The first internal user is {internalUser.FirstName} {internalUser.LastName}"); ``` -------------------------------- ### Write Latest Aggregate to HTTP Response (Guid ID) Source: https://github.com/jasperfx/marten/blob/master/docs/documents/aspnetcore.md Streams the raw JSON of a Guid-identified projected aggregate to the HTTP response without deserialization or serialization when the projection is stored inline. Use this when your aggregate stream is identified by a Guid. ```csharp using Marten; using Microsoft.AspNetCore.Mvc; [HttpGet("/order/{orderId:guid}")] public Task GetOrder(Guid orderId, [FromServices] IDocumentSession session) { // Streams the raw JSON of the projected aggregate to the HTTP response // without deserialization/serialization when the projection is stored inline return session.Events.WriteLatest(orderId, HttpContext); } ``` -------------------------------- ### Install Marten.PgVector Package Source: https://github.com/jasperfx/marten/blob/master/docs/postgres/pgvector.md Add the Marten.PgVector NuGet package to your project. ```shell dotnet add package Marten.PgVector ``` -------------------------------- ### Demonstrate Mixed Tenancy and Non-Tenancy Source: https://github.com/jasperfx/marten/blob/master/docs/documents/multi-tenancy.md This C# snippet shows how to configure a DocumentStore for mixed multi-tenancy, persist documents to specific tenants, non-tenanted documents, and documents to the default tenant. It then demonstrates querying these documents using sessions scoped to different tenants. ```csharp using var store = DocumentStore.For(opts => { opts.DatabaseSchemaName = "mixed_multi_tenants"; opts.Connection(ConnectionSource.ConnectionString); opts.Schema.For().MultiTenanted(); // tenanted opts.Schema.For(); // non-tenanted opts.Schema.For().MultiTenanted(); // tenanted }); await store.Advanced.Clean.DeleteAllDocumentsAsync(); // Add documents to tenant Green var greens = Target.GenerateRandomData(10).ToArray(); await store.BulkInsertAsync("Green", greens); // Add documents to tenant Red var reds = Target.GenerateRandomData(11).ToArray(); await store.BulkInsertAsync("Red", reds); // Add non-tenanted documents // User is non-tenanted in schema var user1 = new User { UserName = "Frank" }; var user2 = new User { UserName = "Bill" }; await store.BulkInsertAsync(new[] { user1, user2 }); // Add documents to default tenant // Note that schema for Issue is multi-tenanted hence documents will get added // to default tenant if tenant is not passed in the bulk insert operation var issue1 = new Issue { Title = "Test issue1" }; var issue2 = new Issue { Title = "Test issue2" }; await store.BulkInsertAsync(new[] { issue1, issue2 }); // Create a session with tenant Green using (var session = store.QuerySession("Green")) { // Query tenanted document as the tenant passed in session (await session.Query().CountAsync()).ShouldBe(10); // Query non-tenanted documents (await session.Query().CountAsync()).ShouldBe(2); // Query documents in default tenant from a session using tenant Green (await session.Query().CountAsync(x => x.TenantIsOneOf(StorageConstants.DefaultTenantId))).ShouldBe(2); // Query documents from tenant Red from a session using tenant Green (await session.Query().CountAsync(x => x.TenantIsOneOf("Red"))).ShouldBe(11); } // create a session without passing any tenant, session will use default tenant using (var session = store.QuerySession()) { // Query non-tenanted documents (await session.Query().CountAsync()).ShouldBe(2); // Query documents in default tenant // Note that session is using default tenant (await session.Query().CountAsync()).ShouldBe(2); // Query documents on tenant Green (await session.Query().CountAsync(x => x.TenantIsOneOf("Green"))).ShouldBe(10); // Query documents on tenant Red (await session.Query().CountAsync(x => x.TenantIsOneOf("Red"))).ShouldBe(11); } ``` -------------------------------- ### Custom Aggregate with Start, Stop, and Increment Logic Source: https://github.com/jasperfx/marten/blob/master/docs/events/projections/explicit.md Defines a custom projection `StartAndStopProjection` that handles `Start`, `End`, `Restart`, and `Increment` events. It includes logic for soft deletion and re-applying state, demonstrating explicit control over event processing based on the aggregate's current state. ```csharp public partial class StartAndStopProjection: SingleStreamProjection { public StartAndStopProjection() { // This is an optional, but potentially important optimization // for the async daemon so that it sets up an allow list // of the event types that will be run through this projection IncludeType(); IncludeType(); IncludeType(); IncludeType(); } public override (StartAndStopAggregate?, ActionType) DetermineAction(StartAndStopAggregate? snapshot, Guid identity, IReadOnlyList events) { var actionType = ActionType.Store; if (snapshot == null && events.HasNoEventsOfType()) { return (snapshot, ActionType.Nothing); } var eventData = events.ToQueueOfEventData(); while (eventData.Any()) { var data = eventData.Dequeue(); switch (data) { case Start: snapshot = new StartAndStopAggregate { // Have to assign the identity ourselves Id = identity }; break; case Increment when snapshot is { Deleted: false }: if (actionType == ActionType.StoreThenSoftDelete) continue; // Use explicit code to only apply this event // if the snapshot already exists snapshot.Increment(); break; case End when snapshot is { Deleted: false }: // This will be a "soft delete" because the snapshot type // implements the IDeleted interface snapshot.Deleted = true; actionType = ActionType.StoreThenSoftDelete; break; case Restart when snapshot == null || snapshot.Deleted: // Got to "undo" the soft delete status actionType = ActionType.UnDeleteAndStore; snapshot.Deleted = false; break; } } return (snapshot, actionType); } } ``` -------------------------------- ### Initialize Marten Database Source: https://github.com/jasperfx/marten/blob/master/CONTRIBUTING.md Use this command to initialize the Marten database with the default test configuration. Requires .NET 6.0 SDK. ```bash dotnet run --framework net6.0 -- init-db ``` -------------------------------- ### JSON Representation of Shipment Source: https://github.com/jasperfx/marten/blob/master/docs/tutorials/getting-started.md Example of how the Shipment document is stored as JSON in PostgreSQL. ```json { "Id": "3a1f...d45", "Origin": "Rotterdam", "Destination": "New York", "Status": "Scheduled", "ScheduledAt": "2025-03-21T08:30:00Z", "PickedUpAt": null, "DeliveredAt": null, "CancelledAt": null } ``` -------------------------------- ### Add Marten.MemoryPack NuGet Package Source: https://github.com/jasperfx/marten/blob/master/docs/events/binary-serialization.md Install the companion NuGet package for MemoryPack integration. ```shell dotnet add package Marten.MemoryPack ``` -------------------------------- ### Eagerly Build All Mappings Source: https://github.com/jasperfx/marten/blob/master/docs/migration-guide.md To ensure validation errors surface at host-build time instead of on first session, call this method eagerly at boot. ```csharp store.Storage.BuildAllMappings(); ``` -------------------------------- ### Configure Per-Tenant Event Partitioning Source: https://github.com/jasperfx/marten/blob/master/docs/events/multitenancy.md Enable per-tenant event partitioning by setting TenancyStyle to Conjoined, AppendMode to Quick, and UseTenantPartitionedEvents to true during DocumentStore configuration. ```csharp var store = DocumentStore.For(opts => { opts.Connection("some connection string"); // Per-tenant partitioning requires conjoined event tenancy opts.Events.TenancyStyle = TenancyStyle.Conjoined; // Per-tenant partitioning only supports the "quick" append modes opts.Events.AppendMode = EventAppendMode.Quick; // Opt into per-tenant event partitioning opts.Events.UseTenantPartitionedEvents = true; }); ``` -------------------------------- ### Add Marten Package using Paket Source: https://github.com/jasperfx/marten/blob/master/docs/getting-started.md Install the Marten NuGet package using Paket. ```shell dotnet paket add Marten ``` -------------------------------- ### Use a Query Plan with QueryByPlanAsync Source: https://github.com/jasperfx/marten/blob/master/docs/documents/querying/compiled-queries.md Demonstrates how to execute a defined query plan asynchronously using the `QueryByPlanAsync` extension method on `IQuerySession`. ```cs public static async Task use_query_plan(IQuerySession session, CancellationToken token) { var targets = await session .QueryByPlanAsync(new ColorTargets(Colors.Blue), token); } ``` -------------------------------- ### Define Customer and Order Document Types Source: https://github.com/jasperfx/marten/blob/master/docs/documents/querying/linq/group-join.md These are the C# classes representing the document types used in the examples. ```csharp public class Customer { public Guid Id { get; set; } public string Name { get; set; } public string City { get; set; } } public class Order { public Guid Id { get; set; } public Guid CustomerId { get; set; } public string Status { get; set; } public decimal Amount { get; set; } } ``` -------------------------------- ### Basic DocumentStore Configuration Source: https://github.com/jasperfx/marten/blob/master/docs/configuration/storeoptions.md This is a foundational builder method for creating a DocumentStore with custom configuration. It accepts an Action delegate to modify the StoreOptions. ```cs public static DocumentStore For(Action configure) { var options = new StoreOptions(); configure(options); return new DocumentStore(options); } ``` -------------------------------- ### Add Marten Package using PowerShell Source: https://github.com/jasperfx/marten/blob/master/docs/getting-started.md Install the Marten NuGet package using the PowerShell Package Manager. ```powershell PM> Install-Package Marten ``` -------------------------------- ### Add Marten Package using .NET CLI Source: https://github.com/jasperfx/marten/blob/master/docs/getting-started.md Install the Marten NuGet package using the .NET CLI. ```shell dotnet add package Marten ``` -------------------------------- ### Configure Document Store with Initial Data Source: https://github.com/jasperfx/marten/blob/master/docs/documents/initial-data.md This C# code shows how to configure the Marten document store to use initial data implementations during application startup. It uses `AddMarten` and `InitializeWith` to register custom `IInitialData` providers. ```csharp using var host = await Host.CreateDefaultBuilder() .ConfigureServices(services => { services.AddMarten(opts => { opts.DatabaseSchemaName = "Bug962"; opts.Connection(ConnectionSource.ConnectionString); }) // Add as many implementations of IInitialData as you need .InitializeWith(new InitialData(InitialDatasets.Companies), new InitialData(InitialDatasets.Users)); }).StartAsync(); var store = host.Services.GetRequiredService(); ``` -------------------------------- ### Rebuild a Single Projection Asynchronously Source: https://github.com/jasperfx/marten/blob/master/docs/events/projections/rebuilding.md Demonstrates how to rebuild a specific projection named 'Shop' using the Marten async daemon. Ensure the IDocumentStore is initialized before use. ```csharp private IDocumentStore _store; public RebuildRunner(IDocumentStore store) { _store = store; } public async Task RunRebuildAsync() { using var daemon = await _store.BuildProjectionDaemonAsync(); await daemon.RebuildProjectionAsync("Shop", CancellationToken.None); } ```