diff --git a/.github/workflows/on-push-do-ci-build-pg15-jsonnet-eventsourcing-closed-shape.yml b/.github/workflows/on-push-do-ci-build-pg15-jsonnet-eventsourcing-closed-shape.yml
deleted file mode 100644
index 10d04bdd94..0000000000
--- a/.github/workflows/on-push-do-ci-build-pg15-jsonnet-eventsourcing-closed-shape.yml
+++ /dev/null
@@ -1,92 +0,0 @@
-name: Build & Test - NET9 - PG15 - Newtonsoft - EventSourcing (Closed-Shape)
-
-# #4430: parallel CI lane that runs the event-sourcing suite with the
-# closed-shape event-storage flag on (StoreOptions.Events.UseClosedShapeStorage = true).
-# Guards against drift between codegen + closed-shape coverage so the v10 default
-# flip lands clean. Same lane on the PG15 + Newtonsoft matrix entry so
-# partition-related regressions (which depend on the older PG line as well as
-# the serializer) get caught.
-
-on:
- push:
- branches:
- - master
- - "7.0"
- paths-ignore:
- - 'documentation/**'
- - 'docs/**'
- - 'azure-pipelines.yml'
- pull_request:
- branches:
- - master
- - "7.0"
- paths-ignore:
- - 'documentation/**'
- - 'docs/**'
- - 'azure-pipelines.yml'
-
-env:
- DOTNET_CLI_TELEMETRY_OPTOUT: 1
- DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
- pg_db: marten_testing
- pg_user: postgres
- CONFIGURATION: Release
- FRAMEWORK: net9.0
- DISABLE_TEST_PARALLELIZATION: true
- DEFAULT_SERIALIZER: "Newtonsoft"
- NUKE_TELEMETRY_OPTOUT: true
- MARTEN_USE_CLOSED_SHAPE_STORAGE: true
-
-jobs:
- build:
- runs-on: ubuntu-latest
- timeout-minutes: 20
-
- services:
- postgres:
- image: postgres:15-alpine
- ports:
- - 5432:5432
- env:
- POSTGRES_HOST_AUTH_METHOD: trust
- POSTGRES_DB: ${{ env.pg_db }}
- NAMEDATALEN: 150
- options: >-
- --health-cmd pg_isready
- --health-interval 10s
- --health-timeout 5s
- --health-retries 5
- --user postgres
-
- steps:
- - uses: actions/checkout@v6
-
- - name: Install .NET
- uses: actions/setup-dotnet@v5
- with:
- dotnet-version: |
- 9.0.x
- 10.0.x
-
- - name: Install .NET Aspire workload
- run: |
- dotnet workload update
- dotnet workload install aspire
-
- - name: Optimize database for running tests faster
- run: |
- PG_CONTAINER_NAME=$(docker ps --filter expose=5432/tcp --format {{.Names}})
- docker exec $PG_CONTAINER_NAME bash -c "echo -e '\nfsync = off' >> /var/lib/postgresql/data/postgresql.conf"
- docker exec $PG_CONTAINER_NAME bash -c "echo -e '\nfull_page_writes = off' >> /var/lib/postgresql/data/postgresql.conf"
- docker exec $PG_CONTAINER_NAME bash -c "echo -e '\nsynchronous_commit = off' >> /var/lib/postgresql/data/postgresql.conf"
- docker container restart $PG_CONTAINER_NAME
- shell: bash
-
- - name: compile
- run: ./build.sh compile-project --project src/EventSourcingTests/EventSourcingTests.csproj
- shell: bash
-
- - name: test-event-sourcing
- if: ${{ success() || failure() }}
- run: ./build.sh test-event-sourcing
- shell: bash
diff --git a/.github/workflows/on-push-do-ci-build-pgLatest-systemtextjson-eventsourcing-closed-shape.yml b/.github/workflows/on-push-do-ci-build-pgLatest-systemtextjson-eventsourcing-closed-shape.yml
deleted file mode 100644
index 0cdefc33b1..0000000000
--- a/.github/workflows/on-push-do-ci-build-pgLatest-systemtextjson-eventsourcing-closed-shape.yml
+++ /dev/null
@@ -1,90 +0,0 @@
-name: Build & Test - NET10 - PGLatest - System.Text.Json - Event Sourcing (Closed-Shape)
-
-# #4430: parallel CI lane that runs the event-sourcing suite with the
-# closed-shape event-storage flag on (StoreOptions.Events.UseClosedShapeStorage = true).
-# Guards against drift between codegen + closed-shape coverage so the v10 default
-# flip lands clean.
-
-on:
- push:
- branches:
- - master
- - "7.0"
- paths-ignore:
- - 'documentation/**'
- - 'docs/**'
- - 'azure-pipelines.yml'
- pull_request:
- branches:
- - master
- - "7.0"
- paths-ignore:
- - 'documentation/**'
- - 'docs/**'
- - 'azure-pipelines.yml'
-
-env:
- DOTNET_CLI_TELEMETRY_OPTOUT: 1
- DOTNET_SKIP_FIRST_TIME_EXPERIENCE: 1
- pg_db: marten_testing
- pg_user: postgres
- CONFIGURATION: Release
- FRAMEWORK: net10.0
- DISABLE_TEST_PARALLELIZATION: true
- DEFAULT_SERIALIZER: "SystemTextJson"
- NUKE_TELEMETRY_OPTOUT: true
- MARTEN_USE_CLOSED_SHAPE_STORAGE: true
-
-jobs:
- build:
- runs-on: ubuntu-latest
- timeout-minutes: 20
-
- services:
- postgres:
- image: postgres:latest
- ports:
- - 5432:5432
- env:
- POSTGRES_HOST_AUTH_METHOD: trust
- POSTGRES_DB: ${{ env.pg_db }}
- NAMEDATALEN: 150
- options: >-
- --health-cmd pg_isready
- --health-interval 10s
- --health-timeout 5s
- --health-retries 5
- --user postgres
-
- steps:
- - uses: actions/checkout@v6
-
- - name: Install .NET
- uses: actions/setup-dotnet@v5
- with:
- dotnet-version: |
- 9.0.x
- 10.0.x
-
- - name: Install .NET Aspire workload
- run: |
- dotnet workload update
- dotnet workload install aspire
-
- # - name: Optimize database for running tests faster
- # run: |
- # PG_CONTAINER_NAME=$(docker ps --filter expose=5432/tcp --format {{.Names}})
- # docker exec $PG_CONTAINER_NAME bash -c "echo -e '\nfsync = off' >> /var/lib/postgresql/data/postgresql.conf"
- # docker exec $PG_CONTAINER_NAME bash -c "echo -e '\nfull_page_writes = off' >> /var/lib/postgresql/data/postgresql.conf"
- # docker exec $PG_CONTAINER_NAME bash -c "echo -e '\nsynchronous_commit = off' >> /var/lib/postgresql/data/postgresql.conf"
- # docker container restart $PG_CONTAINER_NAME
- # shell: bash
-
- - name: compile
- run: ./build.sh compile-project --project src/EventSourcingTests/EventSourcingTests.csproj
- shell: bash
-
- - name: test-event-sourcing
- if: ${{ success() || failure() }}
- run: ./build.sh test-event-sourcing
- shell: bash
diff --git a/src/DaemonTests/Bugs/Bug_4428_rich_storage_side_effect_events.cs b/src/DaemonTests/Bugs/Bug_4428_rich_storage_side_effect_events.cs
index 277238292f..50b86ae551 100644
--- a/src/DaemonTests/Bugs/Bug_4428_rich_storage_side_effect_events.cs
+++ b/src/DaemonTests/Bugs/Bug_4428_rich_storage_side_effect_events.cs
@@ -17,8 +17,7 @@ namespace DaemonTests.Bugs;
///
/// Regression for jasperfx/marten#4428.
///
-/// When StoreOptions.Events.UseClosedShapeStorage = true and
-/// AppendMode = EventAppendMode.Rich, the async-projection
+/// Under AppendMode = EventAppendMode.Rich, the async-projection
/// side-effect replay path (JasperFx.Events EventSlice.BuildOperations
/// → IProjectionBatch.QuickAppendEventWithVersion →
/// Marten ProjectionBatch.QuickAppendEventWithVersion →
@@ -27,8 +26,8 @@ namespace DaemonTests.Bugs;
/// NotImplementedException. The closed-shape Rich implementation was
/// stubbed out and the assumption "the Rich appender only calls AppendEvent"
/// missed the daemon's side-effect replay path. This test asserts the
-/// raised side-effect event lands in mt_events when running the rebuild
-/// under UseClosedShapeStorage = true + Rich.
+/// raised side-effect event lands in mt_events when running a Rich-mode
+/// rebuild.
///
public class Bug_4428_rich_storage_side_effect_events: OneOffConfigurationsContext
{
@@ -37,10 +36,6 @@ public async Task raised_side_effect_event_persists_under_closed_shape_rich()
{
StoreOptions(opts =>
{
- // Force the closed-shape Rich path explicitly so the test reproduces
- // #4428 regardless of the suite-wide env flag (which only affects
- // UseClosedShapeStorage, not AppendMode).
- opts.Events.UseClosedShapeStorage = true;
opts.Events.AppendMode = EventAppendMode.Rich;
opts.Projections.Add(ProjectionLifecycle.Async);
});
diff --git a/src/EventSourcingTests/Bugs/Bug_4411_closed_shape_read_side.cs b/src/EventSourcingTests/Bugs/Bug_4411_closed_shape_read_side.cs
index f90676ce31..0cf97b2927 100644
--- a/src/EventSourcingTests/Bugs/Bug_4411_closed_shape_read_side.cs
+++ b/src/EventSourcingTests/Bugs/Bug_4411_closed_shape_read_side.cs
@@ -24,11 +24,12 @@ namespace EventSourcingTests.Bugs;
///
///
///
-/// We can't yet flip UseClosedShapeStorage on end-to-end because
-/// the closed-shape write path is still stubbed (#4413 / #4414). So the
-/// test uses the codegen path to append events, then constructs a
-/// directly and runs the same
-/// select-from-mt_events query against it, comparing the resolved
+/// Originally written before the closed-shape write path was wired
+/// (when only the read side could be exercised), the test still pins
+/// the read-side adapter behavior: it uses the standard append path to
+/// insert events, then constructs a
+/// directly and runs the
+/// same select-from-mt_events query against it, comparing the resolved
/// IEvents field-by-field.
///
///
diff --git a/src/EventSourcingTests/Bugs/Bug_4412_closed_shape_rich_write_path.cs b/src/EventSourcingTests/Bugs/Bug_4412_closed_shape_rich_write_path.cs
index c77de319d5..724ea561aa 100644
--- a/src/EventSourcingTests/Bugs/Bug_4412_closed_shape_rich_write_path.cs
+++ b/src/EventSourcingTests/Bugs/Bug_4412_closed_shape_rich_write_path.cs
@@ -12,10 +12,7 @@ namespace EventSourcingTests.Bugs;
///
/// End-to-end smoke test for the closed-shape Rich-mode write path
-/// (#4412 + #4413): flips UseClosedShapeStorage = true, sets
-/// AppendMode = Rich (the closed-shape adapter currently only wires
-/// the Rich path's write operations — Quick / QuickWithServerTimestamps
-/// land in #4414 / #4415), and exercises:
+/// (#4412 + #4413): sets AppendMode = Rich and exercises:
///
///
/// - via
@@ -35,7 +32,6 @@ public async Task start_stream_then_append_then_read_back_round_trip_guid_identi
{
StoreOptions(opts =>
{
- opts.Events.UseClosedShapeStorage = true;
opts.Events.AppendMode = EventAppendMode.Rich;
});
@@ -101,7 +97,6 @@ public async Task start_stream_then_append_then_read_back_round_trip_string_iden
{
StoreOptions(opts =>
{
- opts.Events.UseClosedShapeStorage = true;
opts.Events.AppendMode = EventAppendMode.Rich;
opts.Events.StreamIdentity = StreamIdentity.AsString;
});
diff --git a/src/EventSourcingTests/Bugs/Bug_4414_closed_shape_quick_write_path.cs b/src/EventSourcingTests/Bugs/Bug_4414_closed_shape_quick_write_path.cs
index 008cb007f4..2449705ffc 100644
--- a/src/EventSourcingTests/Bugs/Bug_4414_closed_shape_quick_write_path.cs
+++ b/src/EventSourcingTests/Bugs/Bug_4414_closed_shape_quick_write_path.cs
@@ -25,7 +25,6 @@ public async Task quick_round_trip_guid_identity()
{
StoreOptions(opts =>
{
- opts.Events.UseClosedShapeStorage = true;
opts.Events.AppendMode = EventAppendMode.Quick;
});
@@ -72,7 +71,6 @@ public async Task quick_round_trip_string_identity()
{
StoreOptions(opts =>
{
- opts.Events.UseClosedShapeStorage = true;
opts.Events.AppendMode = EventAppendMode.Quick;
opts.Events.StreamIdentity = StreamIdentity.AsString;
});
@@ -113,7 +111,6 @@ public async Task quick_round_trip_with_scalar_metadata()
{
StoreOptions(opts =>
{
- opts.Events.UseClosedShapeStorage = true;
opts.Events.AppendMode = EventAppendMode.Quick;
opts.Events.MetadataConfig.CausationIdEnabled = true;
opts.Events.MetadataConfig.CorrelationIdEnabled = true;
diff --git a/src/EventSourcingTests/Bugs/Bug_4415_closed_shape_quick_with_server_timestamps.cs b/src/EventSourcingTests/Bugs/Bug_4415_closed_shape_quick_with_server_timestamps.cs
index 88cc0b04d9..38cbb05d06 100644
--- a/src/EventSourcingTests/Bugs/Bug_4415_closed_shape_quick_with_server_timestamps.cs
+++ b/src/EventSourcingTests/Bugs/Bug_4415_closed_shape_quick_with_server_timestamps.cs
@@ -30,7 +30,6 @@ public async Task quick_with_server_timestamps_round_trip_guid_identity()
{
StoreOptions(opts =>
{
- opts.Events.UseClosedShapeStorage = true;
// AppendMode defaults to QuickWithServerTimestamps in v9, but
// be explicit here to pin the test against the right path.
opts.Events.AppendMode = EventAppendMode.QuickWithServerTimestamps;
@@ -80,7 +79,6 @@ public async Task quick_with_server_timestamps_default_v9_config_works()
// make no other config tweaks. This is the headline "drop-in" path.
StoreOptions(opts =>
{
- opts.Events.UseClosedShapeStorage = true;
});
var streamId = Guid.NewGuid();
diff --git a/src/EventSourcingTests/Bugs/Bug_4416_closed_shape_metadata_binders.cs b/src/EventSourcingTests/Bugs/Bug_4416_closed_shape_metadata_binders.cs
index 4effe1c6e1..30765446f5 100644
--- a/src/EventSourcingTests/Bugs/Bug_4416_closed_shape_metadata_binders.cs
+++ b/src/EventSourcingTests/Bugs/Bug_4416_closed_shape_metadata_binders.cs
@@ -30,7 +30,6 @@ public async Task scalar_metadata_binders_round_trip_under_closed_shape_storage(
{
StoreOptions(opts =>
{
- opts.Events.UseClosedShapeStorage = true;
opts.Events.AppendMode = EventAppendMode.Rich;
opts.Events.MetadataConfig.CausationIdEnabled = true;
opts.Events.MetadataConfig.CorrelationIdEnabled = true;
@@ -76,7 +75,6 @@ public async Task headers_round_trip_under_closed_shape_storage()
// #4416 part 2).
StoreOptions(opts =>
{
- opts.Events.UseClosedShapeStorage = true;
opts.Events.AppendMode = EventAppendMode.Rich;
opts.Events.MetadataConfig.HeadersEnabled = true;
});
@@ -113,7 +111,6 @@ public async Task event_skipping_flag_does_not_break_rich_closed_shape_path()
// adapter must NOT fail the descriptor build.
StoreOptions(opts =>
{
- opts.Events.UseClosedShapeStorage = true;
opts.Events.AppendMode = EventAppendMode.Rich;
opts.Events.EnableEventSkippingInProjectionsOrSubscriptions = true;
});
@@ -139,7 +136,6 @@ public async Task all_metadata_binders_together_round_trip()
// binder wired in #4416 part 1 + part 2.
StoreOptions(opts =>
{
- opts.Events.UseClosedShapeStorage = true;
opts.Events.AppendMode = EventAppendMode.Rich;
opts.Events.MetadataConfig.CausationIdEnabled = true;
opts.Events.MetadataConfig.CorrelationIdEnabled = true;
diff --git a/src/EventSourcingTests/Bugs/Bug_4417_closed_shape_dcb_hstore.cs b/src/EventSourcingTests/Bugs/Bug_4417_closed_shape_dcb_hstore.cs
index 0d989cede2..df174fc9c2 100644
--- a/src/EventSourcingTests/Bugs/Bug_4417_closed_shape_dcb_hstore.cs
+++ b/src/EventSourcingTests/Bugs/Bug_4417_closed_shape_dcb_hstore.cs
@@ -42,7 +42,6 @@ public async Task hstore_tags_round_trip_under_closed_shape_flag()
{
StoreOptions(opts =>
{
- opts.Events.UseClosedShapeStorage = true;
opts.Events.AddEventType();
opts.Events.AddEventType();
@@ -101,7 +100,6 @@ public async Task hstore_tags_round_trip_with_quick_append_mode()
// code paths.)
StoreOptions(opts =>
{
- opts.Events.UseClosedShapeStorage = true;
opts.Events.AppendMode = EventAppendMode.Quick;
opts.Events.AddEventType();
diff --git a/src/EventSourcingTests/QuickAppend/quick_appending_events_workflow_specs.cs b/src/EventSourcingTests/QuickAppend/quick_appending_events_workflow_specs.cs
index 1cc84c2654..6ce39d1ebf 100644
--- a/src/EventSourcingTests/QuickAppend/quick_appending_events_workflow_specs.cs
+++ b/src/EventSourcingTests/QuickAppend/quick_appending_events_workflow_specs.cs
@@ -10,7 +10,7 @@
using JasperFx.Events;
using Marten;
using Marten.Events;
-using Marten.Events.CodeGeneration;
+using Marten.EventStorage;
using Marten.Events.Operations;
using Marten.Exceptions;
using Marten.Internal;
@@ -57,7 +57,7 @@ public async Task can_fetch_stream_async(TestCase @case)
await @case.StartNewStream();
await using var query = @case.Store.QuerySession();
- var builder = EventDocumentStorageGenerator.GenerateStorage(@case.Store.Options);
+ var builder = new ClosedShapeEventDocumentStorage(@case.Store.Options);
var handler = builder.QueryForStream(@case.ToEventStream());
var state = await query.As().ExecuteHandlerAsync(handler, CancellationToken.None);
@@ -73,7 +73,7 @@ public async Task can_insert_a_new_stream(TestCase @case)
@case.StartNewStream();
var stream = @case.CreateNewStream();
- var builder = EventDocumentStorageGenerator.GenerateStorage(@case.Store.Options);
+ var builder = new ClosedShapeEventDocumentStorage(@case.Store.Options);
var op = builder.InsertStream(stream);
await using var session = @case.Store.LightweightSession();
@@ -92,7 +92,7 @@ public async Task can_update_the_version_of_an_existing_stream_sad_path(TestCase
stream.ExpectedVersionOnServer = 3; // it's actually 4, so this should fail
stream.Version = 10;
- var builder = EventDocumentStorageGenerator.GenerateStorage(@case.Store.Options);
+ var builder = new ClosedShapeEventDocumentStorage(@case.Store.Options);
var op = builder.UpdateStreamVersion(stream);
await using var session = @case.Store.LightweightSession();
diff --git a/src/EventSourcingTests/appending_events_workflow_specs.cs b/src/EventSourcingTests/appending_events_workflow_specs.cs
index a3de14043a..551a9e9fda 100644
--- a/src/EventSourcingTests/appending_events_workflow_specs.cs
+++ b/src/EventSourcingTests/appending_events_workflow_specs.cs
@@ -12,7 +12,7 @@
using JasperFx.Events;
using Marten;
using Marten.Events;
-using Marten.Events.CodeGeneration;
+using Marten.EventStorage;
using Marten.Events.Operations;
using Marten.Exceptions;
using Marten.Internal;
@@ -60,7 +60,7 @@ public async Task can_fetch_stream_async(TestCase @case)
await @case.StartNewStream();
await using var query = @case.Store.QuerySession();
- var builder = EventDocumentStorageGenerator.GenerateStorage(@case.Store.Options);
+ var builder = new ClosedShapeEventDocumentStorage(@case.Store.Options);
var handler = builder.QueryForStream(@case.ToEventStream());
var state = await query.As().ExecuteHandlerAsync(handler, CancellationToken.None);
@@ -76,7 +76,7 @@ public async Task can_insert_a_new_stream(TestCase @case)
await @case.StartNewStream();
var stream = @case.CreateNewStream();
- var builder = EventDocumentStorageGenerator.GenerateStorage(@case.Store.Options);
+ var builder = new ClosedShapeEventDocumentStorage(@case.Store.Options);
var op = builder.InsertStream(stream);
await using var session = @case.Store.LightweightSession();
@@ -95,7 +95,7 @@ public async Task can_update_the_version_of_an_existing_stream_happy_path(TestCa
stream.ExpectedVersionOnServer = 4;
stream.Version = 10;
- var builder = EventDocumentStorageGenerator.GenerateStorage(@case.Store.Options);
+ var builder = new ClosedShapeEventDocumentStorage(@case.Store.Options);
var op = builder.UpdateStreamVersion(stream);
await using var session = @case.Store.LightweightSession();
@@ -119,7 +119,7 @@ public async Task can_update_the_version_of_an_existing_stream_sad_path(TestCase
stream.ExpectedVersionOnServer = 3; // it's actually 4, so this should fail
stream.Version = 10;
- var builder = EventDocumentStorageGenerator.GenerateStorage(@case.Store.Options);
+ var builder = new ClosedShapeEventDocumentStorage(@case.Store.Options);
var op = builder.UpdateStreamVersion(stream);
await using var session = @case.Store.LightweightSession();
diff --git a/src/Marten.Testing/Harness/DefaultStoreFixture.cs b/src/Marten.Testing/Harness/DefaultStoreFixture.cs
index 4394c17201..a9001d1e23 100644
--- a/src/Marten.Testing/Harness/DefaultStoreFixture.cs
+++ b/src/Marten.Testing/Harness/DefaultStoreFixture.cs
@@ -21,9 +21,6 @@ public class DefaultStoreFixture: IAsyncLifetime
opts.AutoCreateSchemaObjects = AutoCreate.All;
opts.DatabaseSchemaName = "string_events";
opts.Events.StreamIdentity = StreamIdentity.AsString;
-
- if (TestsSettings.UseClosedShapeStorage)
- opts.Events.UseClosedShapeStorage = true;
});
using var conn = new NpgsqlConnection(ConnectionSource.ConnectionString);
@@ -42,9 +39,6 @@ public Task InitializeAsync()
opts.GeneratedCodeMode = TypeLoadMode.Auto;
opts.ApplicationAssembly = GetType().Assembly;
-
- if (TestsSettings.UseClosedShapeStorage)
- opts.Events.UseClosedShapeStorage = true;
});
// Do this exactly once and no more.
diff --git a/src/Marten.Testing/Harness/IntegrationContext.cs b/src/Marten.Testing/Harness/IntegrationContext.cs
index ba4dd68140..cfaff2362f 100644
--- a/src/Marten.Testing/Harness/IntegrationContext.cs
+++ b/src/Marten.Testing/Harness/IntegrationContext.cs
@@ -73,9 +73,6 @@ protected IDocumentStore SeparateStore()
opts.GeneratedCodeMode = TypeLoadMode.Auto;
opts.ApplicationAssembly = GetType().Assembly;
-
- if (TestsSettings.UseClosedShapeStorage)
- opts.Events.UseClosedShapeStorage = true;
});
}
@@ -128,14 +125,6 @@ protected string StoreOptions(Action configure)
configure(options);
- // #4417 / #4418: opt-in suite sweep under the closed-shape adapter.
- // Applied AFTER the test's configure so an individual test that
- // explicitly turns it off wins.
- if (TestsSettings.UseClosedShapeStorage && !options.Events.UseClosedShapeStorage)
- {
- options.Events.UseClosedShapeStorage = true;
- }
-
_store = new DocumentStore(options);
Disposables.Add(_store);
diff --git a/src/Marten.Testing/Harness/OneOffConfigurationsContext.cs b/src/Marten.Testing/Harness/OneOffConfigurationsContext.cs
index 7502986a4e..62f8688520 100644
--- a/src/Marten.Testing/Harness/OneOffConfigurationsContext.cs
+++ b/src/Marten.Testing/Harness/OneOffConfigurationsContext.cs
@@ -49,11 +49,6 @@ protected DocumentStore SeparateStore(Action configure = null)
configure?.Invoke(options);
- if (TestsSettings.UseClosedShapeStorage && !options.Events.UseClosedShapeStorage)
- {
- options.Events.UseClosedShapeStorage = true;
- }
-
var store = new DocumentStore(options);
_disposables.Add(store);
@@ -89,16 +84,6 @@ private DocumentStore storeOptions(Action configure, bool cleanAll
configure(options);
- // #4417 / #4418: run the whole event-sourcing suite under the
- // closed-shape adapter when MARTEN_USE_CLOSED_SHAPE_STORAGE=true.
- // Applied AFTER the test's configure callback so individual tests
- // that explicitly disable the flag (or set conflicting options)
- // win.
- if (TestsSettings.UseClosedShapeStorage && !options.Events.UseClosedShapeStorage)
- {
- options.Events.UseClosedShapeStorage = true;
- }
-
if (cleanAll)
{
using var conn = new NpgsqlConnection(ConnectionSource.ConnectionString);
diff --git a/src/Marten.Testing/Harness/StoreFixture.cs b/src/Marten.Testing/Harness/StoreFixture.cs
index e11c41b44d..446e793cba 100644
--- a/src/Marten.Testing/Harness/StoreFixture.cs
+++ b/src/Marten.Testing/Harness/StoreFixture.cs
@@ -31,11 +31,6 @@ public DocumentStore Store
{
if (_store == null)
{
- if (TestsSettings.UseClosedShapeStorage && !Options.Events.UseClosedShapeStorage)
- {
- Options.Events.UseClosedShapeStorage = true;
- }
-
_store = new DocumentStore(Options);
}
diff --git a/src/Marten.Testing/Harness/TestsSettings.cs b/src/Marten.Testing/Harness/TestsSettings.cs
index 53a001a990..c4b738f2c3 100644
--- a/src/Marten.Testing/Harness/TestsSettings.cs
+++ b/src/Marten.Testing/Harness/TestsSettings.cs
@@ -36,27 +36,5 @@ public static SerializerType SerializerType
}
}
- private static bool? useClosedShapeStorage;
-
- ///
- /// Reads MARTEN_USE_CLOSED_SHAPE_STORAGE. When true, the
- /// harness contexts flip StoreOptions.Events.UseClosedShapeStorage
- /// on after the test's own configure callback runs — so we can
- /// run the whole event-sourcing suite under the closed-shape adapter
- /// (#4417 / #4418) without touching the individual tests.
- ///
- public static bool UseClosedShapeStorage
- {
- get
- {
- if (useClosedShapeStorage.HasValue)
- return useClosedShapeStorage.Value;
-
- var env = Environment.GetEnvironmentVariable("MARTEN_USE_CLOSED_SHAPE_STORAGE");
- useClosedShapeStorage = string.Equals(env, "true", StringComparison.OrdinalIgnoreCase);
- return useClosedShapeStorage.Value;
- }
- }
-
}
}
diff --git a/src/Marten/EventStorage/ClosedShapeEventDocumentStorage.cs b/src/Marten/EventStorage/ClosedShapeEventDocumentStorage.cs
index 74a992130c..43c1cb2b6d 100644
--- a/src/Marten/EventStorage/ClosedShapeEventDocumentStorage.cs
+++ b/src/Marten/EventStorage/ClosedShapeEventDocumentStorage.cs
@@ -18,8 +18,8 @@ namespace Marten.EventStorage;
///
/// Non-codegen subclass that delegates
/// the write-path methods to a closed-shape
-/// instance. Constructed by when
-/// StoreOptions.Events.UseClosedShapeStorage is on.
+/// instance. Constructed by as the sole event
+/// storage adapter in v9.
///
///
///
@@ -36,14 +36,12 @@ namespace Marten.EventStorage;
/// chosen via .
///
///
-/// Read-side note: and
-/// are still codegen-emitted
-/// in v9 even with the closed-shape flag on. The closed-shape work in
-/// W4 covers the write path; the read-side equivalent lives with W5
-/// (Marten.SourceGenerator) — the read-side method body needs per-event
-/// type info that source-gen will provide. Until then, the codegen
-/// path is still required for the read selector — see open question (3)
-/// on #4410.
+/// Read-side /
+/// walk
+/// /
+/// over a column list
+/// derived from ; no codegen is
+/// involved on either the read or the write path.
///
///
internal sealed class ClosedShapeEventDocumentStorage: EventDocumentStorage
diff --git a/src/Marten/EventStorage/Dialects/PostgresEventStoreDialect.cs b/src/Marten/EventStorage/Dialects/PostgresEventStoreDialect.cs
index dfccc01d5f..249be18550 100644
--- a/src/Marten/EventStorage/Dialects/PostgresEventStoreDialect.cs
+++ b/src/Marten/EventStorage/Dialects/PostgresEventStoreDialect.cs
@@ -57,7 +57,7 @@ public RichEventStorageDescriptor BuildRichDescriptor(EventGraph graph, ISeriali
appendEventSqlSuffix: ")",
insertStreamSql: BuildInsertStreamSql(graph),
updateStreamVersionSql: BuildUpdateStreamVersionSql(graph),
- streamStateSelectSql: EventDocumentStorageGenerator.BuildStreamStateSelectSql(graph),
+ streamStateSelectSql: Marten.EventStorage.StreamStateSql.Build(graph),
serializeEventData: e => serializer.ToJson(e.Data),
metadataBinders: metadataBinders)
{
@@ -268,7 +268,7 @@ public QuickEventStorageDescriptor BuildQuickDescriptor(EventGraph graph, ISeria
quickAppendEventsSql: BuildQuickAppendEventsSql(graph, serverTimestamps: false),
insertStreamSql: BuildInsertStreamSql(graph),
updateStreamVersionSql: BuildUpdateStreamVersionSql(graph),
- streamStateSelectSql: EventDocumentStorageGenerator.BuildStreamStateSelectSql(graph),
+ streamStateSelectSql: Marten.EventStorage.StreamStateSql.Build(graph),
serializeEventData: e => serializer.ToJson(e.Data))
{
IsGuidStreamIdentity = isGuid,
@@ -303,7 +303,7 @@ public QuickWithServerTimestampsEventStorageDescriptor BuildQuickWithServerTimes
quickAppendEventsWithServerTimestampsSql: BuildQuickAppendEventsSql(graph, serverTimestamps: true),
insertStreamSql: BuildInsertStreamSql(graph),
updateStreamVersionSql: BuildUpdateStreamVersionSql(graph),
- streamStateSelectSql: EventDocumentStorageGenerator.BuildStreamStateSelectSql(graph),
+ streamStateSelectSql: Marten.EventStorage.StreamStateSql.Build(graph),
serializeEventData: e => serializer.ToJson(e.Data))
{
IsGuidStreamIdentity = isGuid,
diff --git a/src/Marten/EventStorage/StreamStateSql.cs b/src/Marten/EventStorage/StreamStateSql.cs
new file mode 100644
index 0000000000..b736134d00
--- /dev/null
+++ b/src/Marten/EventStorage/StreamStateSql.cs
@@ -0,0 +1,18 @@
+#nullable enable
+using Marten.Events;
+
+namespace Marten.EventStorage;
+
+///
+/// SQL fragment helpers shared between the closed-shape event-storage
+/// adapter and the Postgres dialect SQL builders. Lives here (rather than
+/// the per-mode descriptor classes) because the column ordering is coupled
+/// to 's
+/// implementation —
+/// they must be edited together.
+///
+internal static class StreamStateSql
+{
+ public static string Build(EventGraph graph) =>
+ $"select id, version, type, timestamp, created, is_archived from {graph.DatabaseSchemaName}.mt_streams";
+}
diff --git a/src/Marten/Events/CodeGeneration/CodeGenerationExtensions.cs b/src/Marten/Events/CodeGeneration/CodeGenerationExtensions.cs
deleted file mode 100644
index 3fea115269..0000000000
--- a/src/Marten/Events/CodeGeneration/CodeGenerationExtensions.cs
+++ /dev/null
@@ -1,24 +0,0 @@
-using System;
-using System.Linq;
-using System.Reflection;
-using JasperFx.CodeGeneration;
-using JasperFx.CodeGeneration.Frames;
-using JasperFx.Core.Reflection;
-using JasperFx.Events;
-using Marten.Internal;
-using Weasel.Postgresql;
-
-namespace Marten.Events.CodeGeneration;
-
-public static class CodeGenerationExtensions
-{
- public static void AppendSql(this FramesCollection collection, string sql)
- {
- collection.Code($"{{0}}.{nameof(CommandBuilder.Append)}(\"{sql}\");", Use.Type());
- }
-
- public static void AppendSql(this FramesCollection collection, char sql)
- {
- collection.Code($"{{0}}.{nameof(CommandBuilder.Append)}('{sql}');", Use.Type());
- }
-}
diff --git a/src/Marten/Events/CodeGeneration/EventDocumentStorageGenerator.cs b/src/Marten/Events/CodeGeneration/EventDocumentStorageGenerator.cs
deleted file mode 100644
index 7f168feaab..0000000000
--- a/src/Marten/Events/CodeGeneration/EventDocumentStorageGenerator.cs
+++ /dev/null
@@ -1,418 +0,0 @@
-using System;
-using System.Collections.Generic;
-using System.Data;
-using System.Diagnostics;
-using System.Linq;
-using System.Threading;
-using JasperFx.CodeGeneration;
-using JasperFx.CodeGeneration.Frames;
-using JasperFx.CodeGeneration.Model;
-using JasperFx.Core;
-using JasperFx.Core.Reflection;
-using JasperFx.Events;
-using JasperFx.RuntimeCompiler;
-using Marten.Events.Archiving;
-using Marten.Events.Operations;
-using Marten.Events.Querying;
-using Marten.Events.Schema;
-using Marten.Internal;
-using Marten.Internal.CodeGeneration;
-using Marten.Schema;
-using Marten.Storage;
-using Marten.Storage.Metadata;
-using Npgsql;
-using Weasel.Postgresql;
-using System.Diagnostics.CodeAnalysis;
-
-namespace Marten.Events.CodeGeneration;
-
-[UnconditionalSuppressMessage("Trimming", "IL2026",
- Justification = "Class-level: consumes RUC-annotated members (ISerializer, JasperFx.Events aggregator graph, CloseAndBuildAs / GenericFactoryCache fallbacks, FastExpressionCompiler). Document/event/projection types flow in from StoreOptions / Schema.For() / projection registration and are preserved per the AOT publishing guide; AOT consumers supply a source-generator-backed serializer + pre-generated codegen artifacts.")]
-[UnconditionalSuppressMessage("AOT", "IL3050",
- Justification = "Class-level: uses Type.MakeGenericType / MethodInfo.MakeGenericMethod / Activator.CreateInstance / FastExpressionCompiler — runtime code generation. AOT consumers pre-generate codegen artifacts (codegen write) and supply source-generator-backed serializer impls per the AOT publishing guide.")]
-internal static class EventDocumentStorageGenerator
-{
- private const string StreamStateSelectorTypeName = "GeneratedStreamStateQueryHandler";
- private const string InsertStreamOperationName = "GeneratedInsertStream";
- private const string UpdateStreamVersionOperationName = "GeneratedStreamVersionOperation";
- internal const string EventDocumentStorageTypeName = "GeneratedEventDocumentStorage";
-
- ///
- /// Only for testing support
- ///
- ///
- ///
- public static EventDocumentStorage GenerateStorage(StoreOptions options)
- {
- var assembly =
- new GeneratedAssembly(new GenerationRules(SchemaConstants.MartenGeneratedNamespace + ".EventStore"));
- var builderType = AssembleTypes(options, assembly);
-
-
- var compiler = new AssemblyGenerator();
- compiler.ReferenceAssembly(typeof(IMartenSession).Assembly);
- compiler.Compile(assembly);
-
- Debug.WriteLine(builderType.SourceCode);
-
- return (EventDocumentStorage)Activator.CreateInstance(builderType.CompiledType!, options)!;
- }
-
- public static GeneratedType AssembleTypes(StoreOptions options, GeneratedAssembly assembly)
- {
- assembly.ReferenceAssembly(typeof(EventGraph).Assembly);
-
- var builderType = assembly.AddType(EventDocumentStorageTypeName, typeof(EventDocumentStorage));
-
- buildSelectorMethods(options, builderType);
-
- buildAppendEventOperations(options, assembly, builderType);
-
-
- buildInsertStream(builderType, assembly, options.EventGraph);
-
- buildStreamQueryHandlerType(options.EventGraph, assembly);
-
- buildQueryForStreamMethod(options.EventGraph, builderType);
-
- buildUpdateStreamVersion(builderType, assembly, options.EventGraph);
-
- return builderType;
- }
-
- private static void buildAppendEventOperations(StoreOptions options, GeneratedAssembly assembly,
- GeneratedType builderType)
- {
- var appendEventOperationType = buildAppendEventOperation(options.EventGraph, assembly, AppendMode.Full);
- builderType.MethodFor(nameof(EventDocumentStorage.AppendEvent))
- .Frames.ReturnNewGeneratedTypeObject(appendEventOperationType, "stream", "e");
-
- var quickAppendEventGivenVersion =
- buildAppendEventOperation(options.EventGraph, assembly, AppendMode.QuickWithVersion);
- builderType.MethodFor(nameof(EventDocumentStorage.QuickAppendEventWithVersion))
- .Frames.ReturnNewGeneratedTypeObject(quickAppendEventGivenVersion, "stream", "e");
-
- var quickAppend = buildQuickAppendOperation(options.EventGraph, assembly);
- builderType.MethodFor(nameof(EventDocumentStorage.QuickAppendEvents))
- .Frames.ReturnNewGeneratedTypeObject(quickAppend, "stream");
- }
-
- private static void buildSelectorMethods(StoreOptions options, GeneratedType builderType)
- {
- var sync = builderType.MethodFor(nameof(EventDocumentStorage.ApplyReaderDataToEvent));
- var async = builderType.MethodFor(nameof(EventDocumentStorage.ApplyReaderDataToEventAsync));
-
- // The json data column has to go first
- var table = new EventsTable(options.EventGraph);
- var columns = table.SelectColumns();
-
- for (var i = 3; i < columns.Count; i++)
- {
- columns[i].GenerateSelectorCodeSync(sync, options.EventGraph, i);
- columns[i].GenerateSelectorCodeAsync(async, options.EventGraph, i);
- }
- }
-
- private static GeneratedType buildUpdateStreamVersion(GeneratedType builderType, GeneratedAssembly assembly,
- EventGraph graph)
- {
- var operationType = assembly.AddType(UpdateStreamVersionOperationName, typeof(UpdateStreamVersion));
-
- var sql = $"update {graph.DatabaseSchemaName}.mt_streams ";
-
- var configureCommand = operationType.MethodFor("ConfigureCommand");
- configureCommand.DerivedVariables.Add(
- new Variable(typeof(StreamAction), nameof(UpdateStreamVersion.Stream)));
-
- configureCommand.Frames.AppendSql(sql);
-
- configureCommand.Frames.Code($"var parameterBuilder = {{0}}.{nameof(CommandBuilder.CreateGroupedParameterBuilder)}();", Use.Type());
-
- configureCommand.Frames.AppendSql("set version = ");
- configureCommand.SetParameterFromMember(0, x => x.Version);
-
- configureCommand.Frames.AppendSql(" where id = ");
- if (graph.StreamIdentity == StreamIdentity.AsGuid)
- {
- configureCommand.SetParameterFromMember(1, x => x.Id);
- }
- else
- {
- configureCommand.SetParameterFromMember(1, x => x.Key);
- }
-
- configureCommand.Frames.AppendSql(" and version = ");
- configureCommand.SetParameterFromMember(2, x => x.ExpectedVersionOnServer);
-
- if (graph.TenancyStyle == TenancyStyle.Conjoined)
- {
- configureCommand.Frames.AppendSql($" and {TenantIdColumn.Name} = ");
- new TenantIdColumn().As().GenerateAppendCode(configureCommand, 3);
- }
-
- configureCommand.Frames.AppendSql(" returning version");
-
- builderType.MethodFor(nameof(EventDocumentStorage.UpdateStreamVersion))
- .Frames.Code($"return new {assembly.Namespace}.{UpdateStreamVersionOperationName}({{0}});",
- Use.Type());
-
- return operationType;
- }
-
- private static void buildQueryForStreamMethod(EventGraph graph, GeneratedType builderType)
- {
- var arguments = new List
- {
- graph.StreamIdentity == StreamIdentity.AsGuid
- ? $"stream.{nameof(StreamAction.Id)}"
- : $"stream.{nameof(StreamAction.Key)}"
- };
-
- if (graph.TenancyStyle == TenancyStyle.Conjoined)
- {
- arguments.Add($"stream.{nameof(StreamAction.TenantId)}");
- }
-
- builderType.MethodFor(nameof(EventDocumentStorage.QueryForStream))
- .Frames.Code(
- $"return new {builderType.ParentAssembly.Namespace}.{StreamStateSelectorTypeName}({arguments.Join(", ")});");
- }
-
- private static GeneratedType buildStreamQueryHandlerType(EventGraph graph, GeneratedAssembly assembly)
- {
- var streamQueryHandlerType =
- assembly.AddType(StreamStateSelectorTypeName, typeof(StreamStateQueryHandler));
-
- streamQueryHandlerType.AllInjectedFields.Add(graph.StreamIdentity == StreamIdentity.AsGuid
- ? new InjectedField(typeof(Guid), "streamId")
- : new InjectedField(typeof(string), "streamId"));
-
- buildConfigureCommandMethodForStreamState(graph, streamQueryHandlerType);
-
- // Row reading is no longer codegen'd here. The base StreamStateQueryHandler
- // delegates Handle / HandleAsync to ISelector on IEventStorage,
- // so the SELECT clause and the row read share a single definition site
- // (EventDocumentStorage.StreamStateSelectSql + the explicit interface
- // implementation alongside it). See JasperFx/marten#4347.
- return streamQueryHandlerType;
- }
-
- private static void buildConfigureCommandMethodForStreamState(EventGraph graph,
- GeneratedType streamQueryHandlerType)
- {
- if (graph.TenancyStyle == TenancyStyle.Conjoined)
- {
- streamQueryHandlerType.AllInjectedFields.Add(new InjectedField(typeof(string), "tenantId"));
- }
-
- var configureCommand = streamQueryHandlerType.MethodFor("ConfigureCommand");
-
- // Pull the SELECT clause from the storage so the codegen and any other
- // consumer (e.g. the IEventStore explorer surface) share one definition of
- // "the column list that matches ISelector.Resolve".
- var selectSql = BuildStreamStateSelectSql(graph);
- configureCommand.Frames.AppendSql(selectSql + " where id = ");
-
- var idDbType = graph.StreamIdentity == StreamIdentity.AsGuid ? DbType.Guid : DbType.String;
- configureCommand.Frames.Code($"var parameter1 = builder.{nameof(CommandBuilder.AppendParameter)}(_streamId);");
- configureCommand.Frames.Code("parameter1.DbType = {0};", idDbType);
-
- if (graph.TenancyStyle == TenancyStyle.Conjoined)
- {
- configureCommand.Frames.AppendSql($" and {TenantIdColumn.Name} = ");
- configureCommand.Frames.Code($"var parameter2 = builder.{nameof(CommandBuilder.AppendParameter)}(_tenantId);");
- configureCommand.Frames.Code("parameter2.DbType = {0};", DbType.String);
- }
- }
-
- ///
- /// Single source of truth for the StreamState SELECT clause. Kept as a static
- /// helper alongside the codegen because the column ordering is coupled to
- /// 's
- /// implementation — they must be edited together.
- ///
- internal static string BuildStreamStateSelectSql(EventGraph graph) =>
- $"select id, version, type, timestamp, created, is_archived from {graph.DatabaseSchemaName}.mt_streams";
-
- private static GeneratedType buildAppendEventOperation(EventGraph graph, GeneratedAssembly assembly,
- AppendMode mode)
- {
- var typeName = "AppendEventOperation";
- if (mode != AppendMode.Full)
- {
- typeName += mode.ToString();
- }
-
- var baseType = typeof(AppendEventOperationBase);
- var operationType = assembly.AddType(typeName, baseType);
-
- var configure = operationType.MethodFor(nameof(AppendEventOperationBase.ConfigureCommand));
- configure.DerivedVariables.Add(new Variable(typeof(IEvent), nameof(AppendEventOperationBase.Event)));
- configure.DerivedVariables.Add(new Variable(typeof(StreamAction), nameof(AppendEventOperationBase.Stream)));
-
- var columns = new EventsTable(graph).SelectColumns()
-
- // Hokey, use an explicit model for writeable vs readable columns some day
- .Where(x => x is not IsArchivedColumn).ToList();
-
- // Hokey, but we need to move Sequence to the end
- var sequence = columns.OfType().Single();
- columns.Remove(sequence);
- columns.Add(sequence);
-
- var sql =
- $"insert into {graph.DatabaseSchemaName}.mt_events ({columns.Select(x => x.Name).Join(", ")}) values (";
-
- configure.Frames.AppendSql(sql);
-
- configure.Frames.Code($"var parameterBuilder = {{0}}.{nameof(CommandBuilder.CreateGroupedParameterBuilder)}(',');", Use.Type());
-
- for (var i = 0; i < columns.Count; i++)
- {
- columns[i].GenerateAppendCode(configure, graph, i, mode);
- var valueSql = columns[i].ValueSql(graph, mode);
- if (valueSql != "?")
- configure.Frames.AppendSql($"{(i > 0 ? "," : string.Empty)}{valueSql}");
- }
-
- configure.Frames.AppendSql(')');
-
- return operationType;
- }
-
- private static GeneratedType buildQuickAppendOperation(EventGraph graph, GeneratedAssembly assembly)
- {
- var operationType = assembly.AddType("QuickAppendEventsOperation", typeof(QuickAppendEventsOperationBase));
-
- var table = new EventsTable(graph);
-
- var sql = $"select {graph.DatabaseSchemaName}.mt_quick_append_events(";
-
- var configure = operationType.MethodFor(nameof(QuickAppendEventsOperationBase.ConfigureCommand));
- configure.DerivedVariables.Add(new Variable(typeof(StreamAction), nameof(QuickAppendEventsOperationBase.Stream)));
-
- configure.Frames.AppendSql(sql);
-
- configure.Frames.Code($"var parameterBuilder = {{0}}.{nameof(CommandBuilder.CreateGroupedParameterBuilder)}(',');", Use.Type());
-
- if (graph.StreamIdentity == StreamIdentity.AsGuid)
- {
- configure.Frames.Code("writeId(parameterBuilder);");
- }
- else
- {
- configure.Frames.Code("writeKey(parameterBuilder);");
- }
-
- configure.Frames.Code("writeBasicParameters(parameterBuilder, session);");
-
- if (table.Columns.OfType().Any())
- {
- configure.Frames.Code("writeCausationIds(parameterBuilder);");
- }
-
- if (table.Columns.OfType().Any())
- {
- configure.Frames.Code("writeCorrelationIds(parameterBuilder);");
- }
-
- if (table.Columns.OfType().Any())
- {
- configure.Frames.Code("writeHeaders(parameterBuilder, session);");
- }
-
- if (table.Columns.OfType().Any())
- {
- configure.Frames.Code("writeUserNames(parameterBuilder, session);");
- }
-
- if (graph.AppendMode == EventAppendMode.QuickWithServerTimestamps)
- {
- configure.Frames.Code("writeTimestamps(parameterBuilder);");
- }
-
- // HStore mode writes tags via a follow-up UPDATE; the function signature
- // does not include the per-tag varchar[] parameters in that case.
- if (graph.TagTypes.Count > 0 && graph.DcbStorageMode != DcbStorageMode.HStore)
- {
- configure.Frames.Code("writeAllTagValues(parameterBuilder);");
- }
-
- configure.Frames.AppendSql(')');
-
- return operationType;
- }
-
- private static GeneratedType buildInsertStream(GeneratedType builderType, GeneratedAssembly generatedAssembly,
- EventGraph graph)
- {
- var operationType = generatedAssembly.AddType(InsertStreamOperationName, typeof(InsertStreamBase));
-
- var columns = new StreamsTable(graph)
- .Columns
- .OfType()
- .Where(x => x.Writes)
- .ToArray();
-
- var configureCommand = operationType.MethodFor("ConfigureCommand");
- configureCommand.DerivedVariables.Add(new Variable(typeof(StreamAction), nameof(InsertStreamBase.Stream)));
-
- // Strict identity enforcement: wrap the mt_streams insert in a modifying
- // CTE so we can also INSERT into the non-partitioned mt_streams_identity
- // tracking table in the same prepared statement. Postgres rejects
- // semicolon-separated multi-statement prepared statements ("cannot insert
- // multiple commands into a prepared statement"), so a single statement
- // with a data-modifying CTE is the right shape here.
- // PostgreSQL surfaces unique violations from the chained INSERT with
- // TableName = "mt_streams_identity"; InsertStreamBase.matches() recognizes
- // that name and translates it into ExistingStreamIdCollisionException.
- if (graph.EnableStrictStreamIdentityEnforcement)
- {
- var identityColumnNames = graph.TenancyStyle == TenancyStyle.Conjoined
- ? "tenant_id, id"
- : "id";
-
- var cteHead =
- $"with new_stream as (insert into {graph.DatabaseSchemaName}.mt_streams ({columns.Select(x => x.Name).Join(", ")}) values (";
- configureCommand.Frames.AppendSql(cteHead);
- }
- else
- {
- var sql =
- $"insert into {graph.DatabaseSchemaName}.mt_streams ({columns.Select(x => x.Name).Join(", ")}) values (";
- configureCommand.Frames.AppendSql(sql);
- }
-
- configureCommand.Frames.Code($"var parameterBuilder = {{0}}.{nameof(CommandBuilder.CreateGroupedParameterBuilder)}(',');", Use.Type());
-
- for (var i = 0; i < columns.Length; i++)
- {
- columns[i].GenerateAppendCode(configureCommand, i);
- }
-
- if (graph.EnableStrictStreamIdentityEnforcement)
- {
- var identityColumnNames = graph.TenancyStyle == TenancyStyle.Conjoined
- ? "tenant_id, id"
- : "id";
-
- // Close the CTE's INSERT, return the identity columns, then chain the
- // identity-table INSERT off the CTE so it sees the same row that
- // mt_streams just wrote. No new parameters needed — we're piping
- // values through the CTE.
- configureCommand.Frames.AppendSql(
- $") returning {identityColumnNames}) " +
- $"insert into {graph.DatabaseSchemaName}.{StreamIdentityEnforcementTable.TableName} ({identityColumnNames}) " +
- $"select {identityColumnNames} from new_stream");
- }
- else
- {
- configureCommand.Frames.AppendSql(')');
- }
-
- builderType.MethodFor(nameof(EventDocumentStorage.InsertStream))
- .Frames.ReturnNewGeneratedTypeObject(operationType, "stream");
-
- return operationType;
- }
-}
diff --git a/src/Marten/Events/EventDocumentStorage.cs b/src/Marten/Events/EventDocumentStorage.cs
index b01d3ce5d9..b31f1ca8cc 100644
--- a/src/Marten/Events/EventDocumentStorage.cs
+++ b/src/Marten/Events/EventDocumentStorage.cs
@@ -230,7 +230,7 @@ public abstract IStorageOperation AppendEvent(EventGraph events, IMartenSession
public abstract IQueryHandler QueryForStream(StreamAction stream);
public abstract IStorageOperation UpdateStreamVersion(StreamAction stream);
- public string StreamStateSelectSql => CodeGeneration.EventDocumentStorageGenerator.BuildStreamStateSelectSql(Events);
+ public string StreamStateSelectSql => Marten.EventStorage.StreamStateSql.Build(Events);
StreamState ISelector.Resolve(DbDataReader reader)
{
diff --git a/src/Marten/Events/EventGraph.GeneratesCode.cs b/src/Marten/Events/EventGraph.GeneratesCode.cs
index 32d11cc016..06b5ccb3eb 100644
--- a/src/Marten/Events/EventGraph.GeneratesCode.cs
+++ b/src/Marten/Events/EventGraph.GeneratesCode.cs
@@ -1,65 +1,35 @@
using System;
-using System.Collections.Generic;
-using System.Linq;
using System.Reflection;
using System.Threading.Tasks;
using JasperFx.CodeGeneration;
using JasperFx.Events;
-using Marten.Events.CodeGeneration;
-using Marten.Events.Projections;
using Marten.Internal.Storage;
using System.Diagnostics.CodeAnalysis;
namespace Marten.Events;
#nullable enable
-[UnconditionalSuppressMessage("Trimming", "IL2072",
- Justification = "Class-level: assigns the result of a reflective Type/MethodInfo lookup into a DAM-annotated target. Source types are preserved at the registration boundary.")]
public partial class EventGraph: ICodeFile
{
- private readonly Type _storageType;
-
internal DocumentProvider? Provider { get; private set; }
void ICodeFile.AssembleTypes(GeneratedAssembly assembly)
{
- // #4410 / W4: skip codegen when the closed-shape adapter handles
- // the write path. Note: this only skips event-store codegen —
- // document storage and projection codegen are unaffected.
- if (UseClosedShapeStorage) return;
-
- EventDocumentStorageGenerator.AssembleTypes(Options, assembly);
+ // #4454 Phase 2: event storage is fully closed-shape — no Roslyn emit
+ // for the event-store write path. The ICodeFile contract is preserved
+ // for now so DocumentStore's ICodeFileCollection.BuildFiles aggregation
+ // still walks EventGraph; Phase 5 retires the contract entirely.
}
public bool AttachTypesSynchronously(GenerationRules rules, Assembly assembly, IServiceProvider? services,
string containingNamespace)
{
- // #4410 / W4: when the closed-shape flag is on, skip the codegen
- // lookup entirely and construct the hand-written
- // ClosedShapeEventDocumentStorage adapter directly. The flag is
- // off by default in v9; default flips in v10 and the codegen
- // path is removed in v11.
- if (UseClosedShapeStorage)
- {
- var closedShape = new Marten.EventStorage.ClosedShapeEventDocumentStorage(Options);
- Provider = new DocumentProvider(null, closedShape, closedShape, closedShape, closedShape);
- return true;
- }
-
- var storageType = assembly.FindPreGeneratedType(containingNamespace,
- EventDocumentStorageGenerator.EventDocumentStorageTypeName);
-
- if (storageType == null)
- {
- Provider = null;
- }
- else
- {
- var storage = (EventDocumentStorage)Activator.CreateInstance(storageType, Options)!;
- Provider = new DocumentProvider(null, storage, storage, storage, storage);
- }
-
- return Provider != null;
+ // Closed-shape adapter is constructed directly — no codegen lookup,
+ // no JasperFx.RuntimeCompiler fallback. ProviderGraph triggers this
+ // path on first IEvent storage request.
+ var closedShape = new Marten.EventStorage.ClosedShapeEventDocumentStorage(Options);
+ Provider = new DocumentProvider(null, closedShape, closedShape, closedShape, closedShape);
+ return true;
}
Task ICodeFile.AttachTypes(GenerationRules rules, Assembly assembly, IServiceProvider? services,
diff --git a/src/Marten/Events/EventGraph.cs b/src/Marten/Events/EventGraph.cs
index bd5556ca46..09c795a081 100644
--- a/src/Marten/Events/EventGraph.cs
+++ b/src/Marten/Events/EventGraph.cs
@@ -249,32 +249,6 @@ public override IEvent BuildEvent(object eventData)
public bool EnableUniqueIndexOnEventId { get; set; } = false;
- ///
- /// Opt into the closed-shape event-storage hierarchy added in Marten 9
- /// (#4404 W4 / #4410). When , the event-store
- /// path uses hand-written closed-shape
- /// subclasses instead of the
- /// Roslyn-emitted classes. Default in v9 — flag
- /// flips to default-on in v10 and the codegen path is removed in v11.
- ///
- ///
- ///
- /// Three-mode coverage: Rich (Full + QuickWithVersion) /
- /// Quick / QuickWithServerTimestamps. The mode is picked from
- /// at DocumentStore construction; no
- /// runtime branching on append mode after startup.
- ///
- ///
- /// AOT publishing implication: when the flag is on, the event-store
- /// path no longer needs at
- /// runtime. Today's StoreOptions.AllowRuntimeCodeGeneration
- /// gate still applies to the rest of the codegen surface (document
- /// storage, projections); only the event-store slice migrates with
- /// this flag.
- ///
- ///
- public bool UseClosedShapeStorage { get; set; } = false;
-
private readonly List _ignoredIndexes = new();
public IReadOnlyList IgnoredIndexes => _ignoredIndexes;
diff --git a/src/Marten/Events/IEventStoreOptions.cs b/src/Marten/Events/IEventStoreOptions.cs
index bb226af0a9..e47be4001a 100644
--- a/src/Marten/Events/IEventStoreOptions.cs
+++ b/src/Marten/Events/IEventStoreOptions.cs
@@ -103,20 +103,6 @@ public interface IEventStoreOptions
///
public bool EnableStrictStreamIdentityEnforcement { get; set; }
- ///
- /// Opt into the closed-shape event-storage hierarchy added in
- /// Marten 9 (#4404 W4). When , the event-store
- /// path uses hand-written closed-shape storage classes instead of
- /// the runtime-emitted EventDocumentStorageGenerator output.
- /// Default in v9 — the flag flips to
- /// default-on in v10 and the codegen path is removed in v11.
- ///
- ///
- /// AOT-publishing implication: when the flag is on, the event-store
- /// slice no longer needs JasperFx.RuntimeCompiler at runtime.
- ///
- public bool UseClosedShapeStorage { get; set; }
-
///
/// Optional extension point to receive published messages as a side effect from
/// aggregation projections