From f7da431be15c58e686dfe769feccca6bc28c4fd7 Mon Sep 17 00:00:00 2001 From: "Jeremy D. Miller" Date: Wed, 20 May 2026 09:02:01 -0500 Subject: [PATCH 01/11] Bump JasperFx 2.0.0-rc.2 + Weasel 9.0.0-alpha.8 for the dedupe consume cycle Foundation bump for the Critter Stack 2026 dedupe pillar (jasperfx#214) consume work. JasperFx / JasperFx.Events / SourceGenerator / SourceGeneration -> 2.0.0-rc.2; Weasel.Postgresql / Weasel.Core / Weasel.EntityFrameworkCore -> 9.0.0-alpha.8. These rc/alpha packages lift a batch of types Marten previously forked (TenancyStyle, DeleteStyle, metadata markers, IPatchExpression, the projection-coordinator base, Hi-Lo sequence base, exceptions, etc). This commit only moves the pins; the following commits consume each lifted type and type-forward the old Marten names. The tree does not build green until that consume sequence lands (the lifted types collide with Marten's copies until each is resolved). Co-Authored-By: Claude Opus 4.7 (1M context) --- Directory.Packages.props | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index 15649cc353..529bd7cf06 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -13,13 +13,13 @@ - - - + + + all runtime; build; native; contentfiles; analyzers; buildtransitive - + @@ -66,8 +66,8 @@ - - + + From 60b96c2cba764451a1eab7abc1fa5c6eca9c0b4c Mon Sep 17 00:00:00 2001 From: "Jeremy D. Miller" Date: Wed, 20 May 2026 09:04:47 -0500 Subject: [PATCH 02/11] #4517: consume lifted JasperFx TenancyStyle + DeleteStyle jasperfx#327 lifted TenancyStyle -> JasperFx.MultiTenancy.TenancyStyle and DeleteStyle -> JasperFx.DeleteStyle (ordinals unchanged: Single=0/Conjoined=1, Remove=0/SoftDelete=1). Delete Marten's byte-identical copies and alias the old names via global using, matching the OperationRole / SnapshotLifecycle pattern already in GlobalUsings.cs. Fixed the two fully-qualified Storage.TenancyStyle references + one xmldoc cref. Closes #4517. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/Marten/Events/EventStore.cs | 2 +- src/Marten/Events/Fetching/FetchNaturalKeyPlan.cs | 2 +- src/Marten/Events/Querying/StreamStateSelector.cs | 2 +- src/Marten/GlobalUsings.cs | 5 +++++ src/Marten/Schema/DeleteStyle.cs | 7 ------- src/Marten/Storage/TenancyStyle.cs | 14 -------------- 6 files changed, 8 insertions(+), 24 deletions(-) delete mode 100644 src/Marten/Schema/DeleteStyle.cs delete mode 100644 src/Marten/Storage/TenancyStyle.cs diff --git a/src/Marten/Events/EventStore.cs b/src/Marten/Events/EventStore.cs index 85e049adb0..4184f7535e 100644 --- a/src/Marten/Events/EventStore.cs +++ b/src/Marten/Events/EventStore.cs @@ -64,7 +64,7 @@ public void AssignTagWhere(Expression> expression, object tag _ => CompoundWhereFragment.And(holder.Fragments) }; - var isConjoined = _store.Events.TenancyStyle == Storage.TenancyStyle.Conjoined; + var isConjoined = _store.Events.TenancyStyle == TenancyStyle.Conjoined; if (_store.Events.DcbStorageMode == DcbStorageMode.HStore) { diff --git a/src/Marten/Events/Fetching/FetchNaturalKeyPlan.cs b/src/Marten/Events/Fetching/FetchNaturalKeyPlan.cs index c3f4d4cd2c..6b3262829f 100644 --- a/src/Marten/Events/Fetching/FetchNaturalKeyPlan.cs +++ b/src/Marten/Events/Fetching/FetchNaturalKeyPlan.cs @@ -45,7 +45,7 @@ public FetchNaturalKeyPlan(EventGraph events, NaturalKeyDefinition naturalKey, _naturalKeyTableName = $"{events.DatabaseSchemaName}.mt_natural_key_{naturalKey.AggregateType.Name.ToLowerInvariant()}"; _streamIdColumn = events.StreamIdentity == StreamIdentity.AsGuid ? "stream_id" : "stream_key"; - _isConjoined = events.TenancyStyle == Storage.TenancyStyle.Conjoined; + _isConjoined = events.TenancyStyle == TenancyStyle.Conjoined; _isGlobal = events.GlobalAggregates.Contains(typeof(TDoc)); } diff --git a/src/Marten/Events/Querying/StreamStateSelector.cs b/src/Marten/Events/Querying/StreamStateSelector.cs index 46a8bb25e0..1fe430bec7 100644 --- a/src/Marten/Events/Querying/StreamStateSelector.cs +++ b/src/Marten/Events/Querying/StreamStateSelector.cs @@ -14,7 +14,7 @@ namespace Marten.Events.Querying; /// /// Internal base class for generated stream state query handling. Only the /// ConfigureCommand step (which varies by -/// and ) is codegen'd; the row read is delegated +/// and ) is codegen'd; the row read is delegated /// to 's implementation so /// 's FetchStreamStateAsync shares the same reader as /// any other call site (e.g. the IEventStore explorer) that needs a diff --git a/src/Marten/GlobalUsings.cs b/src/Marten/GlobalUsings.cs index e06625d854..a3fbc8954c 100644 --- a/src/Marten/GlobalUsings.cs +++ b/src/Marten/GlobalUsings.cs @@ -6,3 +6,8 @@ // SnapshotLifecycle consolidated to JasperFx.Events per the dedup audit // (jasperfx#220 / pillar #214). Same pattern as OperationRole above. global using SnapshotLifecycle = JasperFx.Events.Projections.SnapshotLifecycle; +// TenancyStyle + DeleteStyle consolidated to JasperFx per the dedup audit +// (jasperfx#327 / marten#4517 / pillar #214). Ordinals unchanged +// (Single=0/Conjoined=1, Remove=0/SoftDelete=1). Same alias pattern as above. +global using TenancyStyle = JasperFx.MultiTenancy.TenancyStyle; +global using DeleteStyle = JasperFx.DeleteStyle; diff --git a/src/Marten/Schema/DeleteStyle.cs b/src/Marten/Schema/DeleteStyle.cs deleted file mode 100644 index 806a35ff6d..0000000000 --- a/src/Marten/Schema/DeleteStyle.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Marten.Schema; - -public enum DeleteStyle -{ - Remove, - SoftDelete -} diff --git a/src/Marten/Storage/TenancyStyle.cs b/src/Marten/Storage/TenancyStyle.cs deleted file mode 100644 index 4c3eee9949..0000000000 --- a/src/Marten/Storage/TenancyStyle.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Marten.Storage; - -public enum TenancyStyle -{ - /// - /// No multi-tenancy, the default mode - /// - Single, - - /// - /// Multi-tenanted within the same database/schema through a tenant id - /// - Conjoined -} From f0dbd6db7558cb56a7f8e17a934cf4067eddad61 Mon Sep 17 00:00:00 2001 From: "Jeremy D. Miller" Date: Wed, 20 May 2026 09:07:25 -0500 Subject: [PATCH 03/11] #4519: consume lifted SkippedEventsCountObserver + ResilientEventLoader MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit jasperfx#329 lifted SkippedEventsCountObserver, ResilientEventLoader, and EventLoaderException into JasperFx.Events.Daemon. Delete Marten's copies and consume the lifted versions: - MartenDatabase subscribes the lifted SkippedEventsCountObserver (Marten's HWM guard was the one kept upstream — behavior identical). - BuildEventLoader constructs the lifted ResilientEventLoader with its new 3-arg ctor (ResiliencePipeline, IEventLoader inner, IEventDatabase database), passing the shard database. - EventLoaderException now resolves to the lifted type (base changed MartenException -> Exception); the only catch site is the Polly .Handle() in ResilientPipelineBuilderExtensions, which now imports JasperFx.Events.Daemon. Nothing catches it as MartenException. Marten core builds clean. Closes #4519. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/Marten/DocumentStore.EventStore.cs | 2 +- .../HighWater/SkippedEventsCountObserver.cs | 35 ----------- .../Daemon/Internals/EventLoaderException.cs | 18 ------ .../Daemon/Internals/ResilientEventLoader.cs | 58 ------------------- .../ResilientPipelineBuilderExtensions.cs | 1 + 5 files changed, 2 insertions(+), 112 deletions(-) delete mode 100644 src/Marten/Events/Daemon/HighWater/SkippedEventsCountObserver.cs delete mode 100644 src/Marten/Events/Daemon/Internals/EventLoaderException.cs delete mode 100644 src/Marten/Events/Daemon/Internals/ResilientEventLoader.cs diff --git a/src/Marten/DocumentStore.EventStore.cs b/src/Marten/DocumentStore.EventStore.cs index 8fd4f7bbec..cad23dd5bd 100644 --- a/src/Marten/DocumentStore.EventStore.cs +++ b/src/Marten/DocumentStore.EventStore.cs @@ -275,7 +275,7 @@ IEventLoader IEventStore.BuildEventLoader(IE { var filters = buildEventLoaderFilters(filtering).ToArray(); var inner = new EventLoader(this, (MartenDatabase)database, shardOptions, filters); - return new ResilientEventLoader(Options.ResiliencePipeline, inner); + return new ResilientEventLoader(Options.ResiliencePipeline, inner, database); } private IEnumerable buildEventLoaderFilters(EventFilterable filterable) diff --git a/src/Marten/Events/Daemon/HighWater/SkippedEventsCountObserver.cs b/src/Marten/Events/Daemon/HighWater/SkippedEventsCountObserver.cs deleted file mode 100644 index cc9754b200..0000000000 --- a/src/Marten/Events/Daemon/HighWater/SkippedEventsCountObserver.cs +++ /dev/null @@ -1,35 +0,0 @@ -#nullable enable -using System; -using JasperFx.Events.Daemon; -using JasperFx.Events.Projections; - -namespace Marten.Events.Daemon.HighWater; - -/// -/// Patches on the HighWaterMark -/// state when the JasperFx.Events HighWaterAgent publishes a -/// event. The shared -/// helper supplies -/// and -/// but doesn't populate the count, so we fill it here on Marten's side using -/// the most-recent semantic (gap size = Sequence - PreviousGoodMark). -/// -internal sealed class SkippedEventsCountObserver: IObserver -{ - public void OnCompleted() { } - - public void OnError(Exception error) { } - - public void OnNext(ShardState value) - { - if (value.Action != ShardAction.Skipped) return; - if (value.ShardName != ShardState.HighWaterMark) return; - if (value.SkippedEventsCount.HasValue) return; - - var skipped = value.Sequence - value.PreviousGoodMark; - if (skipped > 0) - { - value.SkippedEventsCount = skipped; - } - } -} diff --git a/src/Marten/Events/Daemon/Internals/EventLoaderException.cs b/src/Marten/Events/Daemon/Internals/EventLoaderException.cs deleted file mode 100644 index 317b81e41e..0000000000 --- a/src/Marten/Events/Daemon/Internals/EventLoaderException.cs +++ /dev/null @@ -1,18 +0,0 @@ -using System; -using JasperFx.Events.Projections; -using Marten.Exceptions; -using Marten.Storage; - -namespace Marten.Events.Daemon.Internals; - -/// -/// Marten failed to load events for a projection shard -/// -public class EventLoaderException: MartenException -{ - public EventLoaderException(ShardName name, IMartenDatabase martenDatabase, Exception innerException): base( - $"Failure while trying to load events for projection shard '{name}@{martenDatabase.Identifier}'", - innerException) - { - } -} diff --git a/src/Marten/Events/Daemon/Internals/ResilientEventLoader.cs b/src/Marten/Events/Daemon/Internals/ResilientEventLoader.cs deleted file mode 100644 index 3a739b7c7f..0000000000 --- a/src/Marten/Events/Daemon/Internals/ResilientEventLoader.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System; -using System.Threading; -using System.Threading.Tasks; -using JasperFx; -using JasperFx.Events.Daemon; -using JasperFx.Events.Projections; -using Polly; - -namespace Marten.Events.Daemon.Internals; - -internal class ResilientEventLoader: IEventLoader -{ - private readonly ResiliencePipeline _pipeline; - private readonly EventLoader _inner; - - internal record EventLoadExecution(EventRequest Request, IEventLoader Loader) - { - public async ValueTask ExecuteAsync(CancellationToken token) - { - using var activity = Request.Metrics.TrackLoading(Request); - - try - { - var results = await Loader.LoadAsync(Request, token).ConfigureAwait(false); - return results; - } - catch (Exception e) - { - activity?.AddException(e); - throw; - } - finally - { - activity?.Stop(); - } - } - } - - public ResilientEventLoader(ResiliencePipeline pipeline, EventLoader inner) - { - _pipeline = pipeline; - _inner = inner; - } - - public Task LoadAsync(EventRequest request, CancellationToken token) - { - try - { - var execution = new EventLoadExecution(request, _inner); - return _pipeline.ExecuteAsync(static (x, t) => x.ExecuteAsync(t), - execution, token).AsTask(); - } - catch (Exception e) - { - throw new EventLoaderException(request.Name, _inner.Database, e); - } - } -} diff --git a/src/Marten/Util/ResilientPipelineBuilderExtensions.cs b/src/Marten/Util/ResilientPipelineBuilderExtensions.cs index df52c3647e..99818a72d8 100644 --- a/src/Marten/Util/ResilientPipelineBuilderExtensions.cs +++ b/src/Marten/Util/ResilientPipelineBuilderExtensions.cs @@ -1,5 +1,6 @@ #nullable enable using System; +using JasperFx.Events.Daemon; using Marten.Events.Daemon.Internals; using Marten.Exceptions; using Npgsql; From 81eaf45f376674a70b2f8d1054f3041e5e3937d3 Mon Sep 17 00:00:00 2001 From: "Jeremy D. Miller" Date: Wed, 20 May 2026 09:09:53 -0500 Subject: [PATCH 04/11] #4520: consume lifted JasperFx.Metadata markers (ISoftDeleted/IVersioned/ITracked) jasperfx#330 lifted ISoftDeleted, IVersioned, ITracked into JasperFx.Metadata (ITracked carries Marten's exact non-nullable string shape). Delete Marten's byte-identical copies and alias the old names via global using, matching the TenancyStyle / DeleteStyle pattern. Marten's marker detection (CanBeCastTo, `entity is ISoftDeleted`, the metadata policies) now binds the single canonical JasperFx type, so document detection is unchanged. IRevisioned is intentionally excluded (int-vs-long, handled under #4526/#4528). Closes #4520. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/Marten/GlobalUsings.cs | 6 ++++++ src/Marten/Metadata/ISoftDeleted.cs | 27 --------------------------- src/Marten/Metadata/ITracked.cs | 28 ---------------------------- src/Marten/Metadata/IVersioned.cs | 17 ----------------- 4 files changed, 6 insertions(+), 72 deletions(-) delete mode 100644 src/Marten/Metadata/ISoftDeleted.cs delete mode 100644 src/Marten/Metadata/ITracked.cs delete mode 100644 src/Marten/Metadata/IVersioned.cs diff --git a/src/Marten/GlobalUsings.cs b/src/Marten/GlobalUsings.cs index a3fbc8954c..5f047140b5 100644 --- a/src/Marten/GlobalUsings.cs +++ b/src/Marten/GlobalUsings.cs @@ -11,3 +11,9 @@ // (Single=0/Conjoined=1, Remove=0/SoftDelete=1). Same alias pattern as above. global using TenancyStyle = JasperFx.MultiTenancy.TenancyStyle; global using DeleteStyle = JasperFx.DeleteStyle; +// Metadata markers consolidated to JasperFx.Metadata per the dedup audit +// (jasperfx#330 / marten#4520 / pillar #214). Shapes are byte-identical +// (ITracked uses Marten's non-nullable string). Same alias pattern as above. +global using ISoftDeleted = JasperFx.Metadata.ISoftDeleted; +global using IVersioned = JasperFx.Metadata.IVersioned; +global using ITracked = JasperFx.Metadata.ITracked; diff --git a/src/Marten/Metadata/ISoftDeleted.cs b/src/Marten/Metadata/ISoftDeleted.cs deleted file mode 100644 index 1f6576932e..0000000000 --- a/src/Marten/Metadata/ISoftDeleted.cs +++ /dev/null @@ -1,27 +0,0 @@ -#nullable enable -using System; - -namespace Marten.Metadata; - -/// -/// Optionally implement this interface on your Marten document -/// types to opt into "soft delete" mechanics with the deletion -/// information tracked directly on the documents -/// - -#region sample_ISoftDeleted - -public interface ISoftDeleted -{ - /// - /// Has Marten marked this document as soft deleted - /// - bool Deleted { get; set; } - - /// - /// When was this document marked as deleted by Marten - /// - DateTimeOffset? DeletedAt { get; set; } -} - -#endregion diff --git a/src/Marten/Metadata/ITracked.cs b/src/Marten/Metadata/ITracked.cs deleted file mode 100644 index 1829ebbcd5..0000000000 --- a/src/Marten/Metadata/ITracked.cs +++ /dev/null @@ -1,28 +0,0 @@ -#nullable enable -namespace Marten.Metadata; - -/// -/// Optionally implement this interface to add correlation -/// tracking to your Marten document type with the tracking -/// information available on the documents themselves -/// -public interface ITracked -{ - /// - /// Metadata describing the correlation id for the - /// last system activity to edit this document - /// - string CorrelationId { get; set; } - - /// - /// Metadata describing the causation id for the - /// last system activity to edit this document - /// - string CausationId { get; set; } - - /// - /// Metadata describing the user who last modified - /// this document - /// - string LastModifiedBy { get; set; } -} diff --git a/src/Marten/Metadata/IVersioned.cs b/src/Marten/Metadata/IVersioned.cs deleted file mode 100644 index ecbed30374..0000000000 --- a/src/Marten/Metadata/IVersioned.cs +++ /dev/null @@ -1,17 +0,0 @@ -#nullable enable -using System; - -namespace Marten.Metadata; - -/// -/// Optionally implement this interface on your Marten document -/// types to opt into optimistic concurrency with the version -/// being tracked on the Version property -/// -public interface IVersioned -{ - /// - /// Marten's version for this document - /// - Guid Version { get; set; } -} From d8d37db4bcef15ae80f4c91593ed29444b267c6b Mon Sep 17 00:00:00 2001 From: "Jeremy D. Miller" Date: Wed, 20 May 2026 09:13:10 -0500 Subject: [PATCH 05/11] #4521: consume lifted JasperFx.Events IPatchExpression + RemoveAction jasperfx#331 lifted IPatchExpression (Marten's canonical superset, verbatim) and RemoveAction into JasperFx.Events. Delete Marten's copies; PatchExpression and the Patch() extensions now implement/return the lifted interface (import JasperFx.Events). RemoveAction (enum) is aliased via global using; the open-generic IPatchExpression cannot be aliased so the two consumer files import the namespace directly. The self-returning fluent shape rules out a thin derived-interface shim (implicit interface impl needs exact return-type match), so this is a true consume. Closes #4521. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/Marten/GlobalUsings.cs | 5 + src/Marten/Patching/IPatchExpression.cs | 237 ---------------------- src/Marten/Patching/PatchExpression.cs | 1 + src/Marten/Patching/PatchingExtensions.cs | 1 + src/Marten/Patching/RemoveAction.cs | 14 -- 5 files changed, 7 insertions(+), 251 deletions(-) delete mode 100644 src/Marten/Patching/IPatchExpression.cs delete mode 100644 src/Marten/Patching/RemoveAction.cs diff --git a/src/Marten/GlobalUsings.cs b/src/Marten/GlobalUsings.cs index 5f047140b5..e83f33270c 100644 --- a/src/Marten/GlobalUsings.cs +++ b/src/Marten/GlobalUsings.cs @@ -17,3 +17,8 @@ global using ISoftDeleted = JasperFx.Metadata.ISoftDeleted; global using IVersioned = JasperFx.Metadata.IVersioned; global using ITracked = JasperFx.Metadata.ITracked; +// Patching surface consolidated to JasperFx.Events per the dedup audit +// (jasperfx#331 / marten#4521 / pillar #214). RemoveAction (enum) is aliased here; +// IPatchExpression is an open generic so it cannot be aliased — its consumer +// files import JasperFx.Events directly. +global using RemoveAction = JasperFx.Events.RemoveAction; diff --git a/src/Marten/Patching/IPatchExpression.cs b/src/Marten/Patching/IPatchExpression.cs deleted file mode 100644 index 5a9906da8d..0000000000 --- a/src/Marten/Patching/IPatchExpression.cs +++ /dev/null @@ -1,237 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq.Expressions; - -#nullable enable -namespace Marten.Patching; - -public interface IPatchExpression -{ - /// - /// Set a single field or property value within the persisted JSON data - /// - /// - /// - /// - /// - IPatchExpression Set(string name, TValue value); - - /// - /// Set a single field or property value within the persisted JSON data - /// - /// - /// - /// - /// Path to the parent location - /// - /// - IPatchExpression Set(string name, Expression> expression, TValue value); - - /// - /// Set a single field or property value within the persisted JSON data - /// - /// - /// - /// - /// - IPatchExpression Set(Expression> expression, TValue value); - - /// - /// Copy a single field or property value within the persisted JSON data to one or more destinations - /// - /// - /// - /// - /// - IPatchExpression Duplicate(Expression> expression, params Expression>[] destinations); - - /// - /// Increment a single field or property by adding the increment value - /// to the persisted value - /// - /// - /// - /// - IPatchExpression Increment(Expression> expression, int increment = 1); - - /// - /// Increment a single field or property by adding the increment value - /// to the persisted value - /// - /// - /// - /// - IPatchExpression Increment(Expression> expression, long increment = 1); - - /// - /// Increment a single field or property by adding the increment value - /// to the persisted value - /// - /// - /// - /// - IPatchExpression Increment(Expression> expression, double increment = 1); - - /// - /// Increment a single field or property by adding the increment value - /// to the persisted value - /// - /// - /// - /// - IPatchExpression Increment(Expression> expression, float increment = 1); - - /// - /// Increment a single field or property by adding the increment value - /// to the persisted value - /// - /// - /// - /// - IPatchExpression Increment(Expression> expression, decimal increment = 1); - - /// - /// Append an element to the end of a child collection on the persisted - /// document - /// - /// - /// - /// - /// - IPatchExpression Append(Expression>> expression, TElement element); - - /// - /// Append an element with the specified key to a child dictionary on the persisted document - /// - /// - /// - /// - /// - /// - IPatchExpression Append(Expression>> expression, string key, TElement element); - - /// - /// Append an element to the end of a child collection on the persisted - /// document if the element does not already exist - /// - /// - /// - /// - /// - IPatchExpression AppendIfNotExists(Expression>> expression, TElement element); - - /// - /// Append an element with the specified key to a child dictionary on the persisted document if the key does not already exist - /// - /// - /// - /// - /// - /// - IPatchExpression AppendIfNotExists(Expression>> expression, string key, TElement element); - - /// - /// Append an element to the end of a child collection on the persisted - /// document if the element does not already exist by predicate - /// - /// - /// - /// - /// - /// - IPatchExpression AppendIfNotExists(Expression>> expression, TElement element, Expression> predicate); - - /// - /// Insert an element at the designated index to a child collection on the persisted document - /// - /// - /// - /// - /// - /// - IPatchExpression Insert(Expression>> expression, TElement element, int? index = null); - - /// - /// Insert an element at the designated index to a child collection on the persisted document - /// if the value does not already exist at that index - /// - /// - /// - /// - /// - /// - IPatchExpression InsertIfNotExists(Expression>> expression, TElement element, int? index = null); - - /// - /// Insert an element at the designated index to a child collection on the persisted document - /// if the value does not already exist by predicate - /// - /// - /// - /// - /// - /// - /// - IPatchExpression InsertIfNotExists(Expression>> expression, TElement element, Expression> predicate, int? index = null); - - /// - /// Remove element from a child collection on the persisted document - /// - /// - /// - /// - /// - /// - IPatchExpression Remove(Expression>> expression, TElement element, RemoveAction action = RemoveAction.RemoveFirst); - - /// - /// Remove an element with the specified key from a child dictionary on the persisted document - /// - /// - /// - /// - /// - IPatchExpression Remove(Expression>> expression, string key); - - /// - /// Remove element from a child collection by predicate on the persisted document - /// - /// - /// - /// - /// - /// - IPatchExpression Remove(Expression>> expression, Expression> predicate, RemoveAction action = RemoveAction.RemoveFirst); - - /// - /// Rename a property or field in the persisted JSON document - /// - /// - /// - /// - IPatchExpression Rename(string oldName, Expression> expression); - - /// - /// Delete a removed property or field in the persisted JSON data - /// - /// Redundant property or field name - /// - IPatchExpression Delete(string name); - - /// - /// Delete a removed property or field in the persisted JSON data - /// - /// - /// Redundant property or field name - /// Path to the parent location - /// - IPatchExpression Delete(string name, Expression> expression); - - /// - /// Delete an existing property or field in the persisted JSON data - /// - /// - /// Path to the property or field to delete - /// - IPatchExpression Delete(Expression> expression); -} diff --git a/src/Marten/Patching/PatchExpression.cs b/src/Marten/Patching/PatchExpression.cs index 1c97831de0..33b276b9f6 100644 --- a/src/Marten/Patching/PatchExpression.cs +++ b/src/Marten/Patching/PatchExpression.cs @@ -5,6 +5,7 @@ using System.Linq.Expressions; using System.Text.Json; using JasperFx.Core; +using JasperFx.Events; using Marten.Internal.Sessions; using Marten.Linq.Parsing; using Marten.Util; diff --git a/src/Marten/Patching/PatchingExtensions.cs b/src/Marten/Patching/PatchingExtensions.cs index 9022047e92..a3a32b18bf 100644 --- a/src/Marten/Patching/PatchingExtensions.cs +++ b/src/Marten/Patching/PatchingExtensions.cs @@ -1,5 +1,6 @@ using System; using System.Linq.Expressions; +using JasperFx.Events; using Marten.Internal.Sessions; using Weasel.Postgresql.SqlGeneration; diff --git a/src/Marten/Patching/RemoveAction.cs b/src/Marten/Patching/RemoveAction.cs deleted file mode 100644 index 97ffe3acb4..0000000000 --- a/src/Marten/Patching/RemoveAction.cs +++ /dev/null @@ -1,14 +0,0 @@ -namespace Marten.Patching; - -public enum RemoveAction -{ - /// - /// Remove the first occurrence - /// - RemoveFirst, - - /// - /// Remove all occurrences - /// - RemoveAll -} From f7febd2328c2ed8897e0eb0a9bb7f4e8379ec31a Mon Sep 17 00:00:00 2001 From: "Jeremy D. Miller" Date: Wed, 20 May 2026 09:15:01 -0500 Subject: [PATCH 06/11] #4523: consume lifted JasperFx.Events.IDocumentSchemaResolver MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit jasperfx#333 lifted IDocumentSchemaResolver to JasperFx.Events (contract verbatim from Marten's — all 7 members identical). Delete Marten's copy and alias the old name via global using; StoreOptions still implements it (its explicit interface implementations bind through the alias), and IReadOnlyStoreOptions.Schema keeps its return type. Closes #4523. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/Marten/GlobalUsings.cs | 4 ++ src/Marten/IDocumentSchemaResolver.cs | 70 --------------------------- 2 files changed, 4 insertions(+), 70 deletions(-) delete mode 100644 src/Marten/IDocumentSchemaResolver.cs diff --git a/src/Marten/GlobalUsings.cs b/src/Marten/GlobalUsings.cs index e83f33270c..b3b912324a 100644 --- a/src/Marten/GlobalUsings.cs +++ b/src/Marten/GlobalUsings.cs @@ -22,3 +22,7 @@ // IPatchExpression is an open generic so it cannot be aliased — its consumer // files import JasperFx.Events directly. global using RemoveAction = JasperFx.Events.RemoveAction; +// IDocumentSchemaResolver consolidated to JasperFx.Events per the dedup audit +// (jasperfx#333 / marten#4523 / pillar #214). Contract is verbatim from Marten's; +// StoreOptions still implements it (explicit impls bind via this alias). +global using IDocumentSchemaResolver = JasperFx.Events.IDocumentSchemaResolver; diff --git a/src/Marten/IDocumentSchemaResolver.cs b/src/Marten/IDocumentSchemaResolver.cs deleted file mode 100644 index cd0883288a..0000000000 --- a/src/Marten/IDocumentSchemaResolver.cs +++ /dev/null @@ -1,70 +0,0 @@ -#nullable enable -using System; - -namespace Marten; - -public interface IDocumentSchemaResolver -{ - /// - /// The schema name used to store the documents. - /// - string DatabaseSchemaName { get; } - - /// - /// The database schema name for event related tables. By default this - /// is the same schema as the document storage - /// - string EventsSchemaName { get; } - - /// - /// Find the database name of the table backing . Supports documents and projections. - /// - /// The document or projection to look up. - /// - /// When true (default) the qualified name is returned (schema and table name). - /// Otherwise only the table name is returned. - /// - /// The name of in the database. - string For(bool qualified = true); - - /// - /// Find the database name of the table backing . Supports documents and projections. - /// - /// The document type - /// - /// When true (default) the qualified name is returned (schema and table name). - /// Otherwise only the table name is returned. - /// - /// The name of in the database. - string For(Type documentType, bool qualified = true); - - /// - /// Find the database name of the table backing the events table. Supports documents and projections. - /// - /// - /// When true (default) the qualified name is returned (schema and table name). - /// Otherwise only the table name is returned. - /// - /// The name of events table in the database. - string ForEvents(bool qualified = true); - - /// - /// Find the database name of the table backing the event streams table. Supports documents and projections. - /// - /// - /// When true (default) the qualified name is returned (schema and table name). - /// Otherwise only the table name is returned. - /// - /// The name of event streams table in the database. - string ForStreams(bool qualified = true); - - /// - /// Find the database name of the table backing the event progression table. Supports documents and projections. - /// - /// - /// When true (default) the qualified name is returned (schema and table name). - /// Otherwise only the table name is returned. - /// - /// The name of event progression table in the database. - string ForEventProgression(bool qualified = true); -} From 7864afccf9e686ae6a4f53ea19dccee14c644b8c Mon Sep 17 00:00:00 2001 From: "Jeremy D. Miller" Date: Wed, 20 May 2026 09:17:06 -0500 Subject: [PATCH 07/11] #4522: derive OpenTelemetryOptions from JasperFx base + alias TrackLevel jasperfx#332 lifted the common OpenTelemetryOptions base + TrackLevel to JasperFx.OpenTelemetry. Marten.Services.OpenTelemetryOptions now derives from JasperFx.OpenTelemetry.OpenTelemetryOptions via : base("Marten"), dropping the duplicated TrackConnections + Meter (now inherited) while keeping the changeset-metrics members (Applications, ExportCounterOnChangeSets, TrackEventCounters, MartenCommitMetrics) which use the inherited Meter. The name is unchanged so no type-forward is needed. TrackLevel is aliased via global using. Closes #4522. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/Marten/GlobalUsings.cs | 4 +++ src/Marten/Services/OpenTelemetryOptions.cs | 31 +++------------------ 2 files changed, 8 insertions(+), 27 deletions(-) diff --git a/src/Marten/GlobalUsings.cs b/src/Marten/GlobalUsings.cs index b3b912324a..f8efdde1fc 100644 --- a/src/Marten/GlobalUsings.cs +++ b/src/Marten/GlobalUsings.cs @@ -26,3 +26,7 @@ // (jasperfx#333 / marten#4523 / pillar #214). Contract is verbatim from Marten's; // StoreOptions still implements it (explicit impls bind via this alias). global using IDocumentSchemaResolver = JasperFx.Events.IDocumentSchemaResolver; +// TrackLevel consolidated to JasperFx.OpenTelemetry per the dedup audit +// (jasperfx#332 / marten#4522 / pillar #214). Marten.Services.OpenTelemetryOptions +// derives from the lifted base; TrackLevel is aliased here. +global using TrackLevel = JasperFx.OpenTelemetry.TrackLevel; diff --git a/src/Marten/Services/OpenTelemetryOptions.cs b/src/Marten/Services/OpenTelemetryOptions.cs index 0a664fa7ca..e25c5d7942 100644 --- a/src/Marten/Services/OpenTelemetryOptions.cs +++ b/src/Marten/Services/OpenTelemetryOptions.cs @@ -11,30 +11,11 @@ namespace Marten.Services; -public enum TrackLevel +public sealed class OpenTelemetryOptions: JasperFx.OpenTelemetry.OpenTelemetryOptions { - /// - /// No Open Telemetry tracking - /// - None, - - /// - /// Normal level of Open Telemetry tracking - /// - Normal, - - /// - /// Very verbose event tracking, only suitable for debugging or performance tuning - /// - Verbose -} - -public sealed class OpenTelemetryOptions -{ - /// - /// Used to track OpenTelemetry events for opening an connection or exceptions on a connection, for example when a command or data reader has been executed. This defaults to false. - /// - public TrackLevel TrackConnections { get; set; } = TrackLevel.None; + public OpenTelemetryOptions(): base("Marten") + { + } internal List> Applications { get; } = new(); @@ -71,10 +52,6 @@ public void TrackEventCounters() } }); } - - [IgnoreDescription] - public Meter Meter { get; } = new("Marten"); - } internal class MartenCommitMetrics(ILogger Logger, List> applications): DocumentSessionListenerBase From 39bb86c7c0d40e2f9e85587f7486c1b77eb9f04e Mon Sep 17 00:00:00 2001 From: "Jeremy D. Miller" Date: Wed, 20 May 2026 09:59:07 -0500 Subject: [PATCH 08/11] #4518: consume lifted DcbConcurrencyException + centralize dedupe aliases MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit jasperfx#328 lifted DcbConcurrencyException (now : JasperFx.ConcurrencyException) into JasperFx.Events. Delete Marten's copy; AssertDcbConsistency binds the lifted type (it already imports JasperFx.Events). ProgressionProgressOutOfOrder Exception was already the shared type (Marten used the ShardName ctor) — no change. Because propagating all the dedupe type-forwards (TenancyStyle, DeleteStyle, metadata markers, RemoveAction, IDocumentSchemaResolver, TrackLevel, DcbConcurrencyException, IStorageOperation, OperationRole, SnapshotLifecycle) across Marten's test/extension projects would otherwise duplicate alias blocks in many GlobalUsings.cs files, centralize them in src/Shared/DedupeAliases.cs, linked into every JasperFx-referencing .csproj via Directory.Build.props (ImplicitUsings is off repo-wide, so MSBuild items don't apply — a linked C# global-using file is used). Removed the now-redundant per-project GlobalUsings alias blocks; F#/netstandard2.0/LinqTestsTypes (base-JasperFx only) are excluded. Closes #4518. Co-Authored-By: Claude Opus 4.7 (1M context) --- Directory.Build.props | 10 +++++ src/CoreTests/GlobalUsings.cs | 2 - src/CoreTests/retry_mechanism.cs | 4 -- src/DaemonTests/GlobalUsings.cs | 2 - .../SessionMechanics/UnitOfWorkExtensions.cs | 2 - src/EventSourcingTests/GlobalUsings.cs | 5 --- src/IssueService/GlobalUsings.cs | 2 - src/LinqTestsTypes/LinqTestsTypes.csproj | 3 ++ .../Events/Dcb/DcbConcurrencyException.cs | 22 ----------- src/Marten/GlobalUsings.cs | 32 ---------------- src/Shared/DedupeAliases.cs | 38 +++++++++++++++++++ src/StressTests/GlobalUsings.cs | 2 - src/samples/DocSamples/GlobalUsings.cs | 4 +- 13 files changed, 53 insertions(+), 75 deletions(-) delete mode 100644 src/CoreTests/GlobalUsings.cs delete mode 100644 src/DaemonTests/GlobalUsings.cs delete mode 100644 src/EventSourcingTests/GlobalUsings.cs delete mode 100644 src/IssueService/GlobalUsings.cs delete mode 100644 src/Marten/Events/Dcb/DcbConcurrencyException.cs delete mode 100644 src/Marten/GlobalUsings.cs create mode 100644 src/Shared/DedupeAliases.cs delete mode 100644 src/StressTests/GlobalUsings.cs diff --git a/Directory.Build.props b/Directory.Build.props index c2016bcedd..7995d86f2a 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -16,4 +16,14 @@ + + + + + diff --git a/src/CoreTests/GlobalUsings.cs b/src/CoreTests/GlobalUsings.cs deleted file mode 100644 index 99003119ee..0000000000 --- a/src/CoreTests/GlobalUsings.cs +++ /dev/null @@ -1,2 +0,0 @@ -// SnapshotLifecycle consolidated to JasperFx.Events per the dedup audit (jasperfx#220 / pillar #214). -global using SnapshotLifecycle = JasperFx.Events.Projections.SnapshotLifecycle; diff --git a/src/CoreTests/retry_mechanism.cs b/src/CoreTests/retry_mechanism.cs index 7d13528541..09b418af06 100644 --- a/src/CoreTests/retry_mechanism.cs +++ b/src/CoreTests/retry_mechanism.cs @@ -10,10 +10,6 @@ using Npgsql; using Shouldly; using Weasel.Postgresql; -// OperationRole moved to Weasel.Core per the dedup audit (#4350 / pillar #214). -// Use a file-local alias to avoid pulling in Weasel.Core wholesale (which -// would collide with Marten.Internal.Operations.IStorageOperation). -using OperationRole = Weasel.Core.OperationRole; using Xunit; namespace CoreTests; diff --git a/src/DaemonTests/GlobalUsings.cs b/src/DaemonTests/GlobalUsings.cs deleted file mode 100644 index 99003119ee..0000000000 --- a/src/DaemonTests/GlobalUsings.cs +++ /dev/null @@ -1,2 +0,0 @@ -// SnapshotLifecycle consolidated to JasperFx.Events per the dedup audit (jasperfx#220 / pillar #214). -global using SnapshotLifecycle = JasperFx.Events.Projections.SnapshotLifecycle; diff --git a/src/DocumentDbTests/SessionMechanics/UnitOfWorkExtensions.cs b/src/DocumentDbTests/SessionMechanics/UnitOfWorkExtensions.cs index 8b9f94f21b..b97b1f2f7a 100644 --- a/src/DocumentDbTests/SessionMechanics/UnitOfWorkExtensions.cs +++ b/src/DocumentDbTests/SessionMechanics/UnitOfWorkExtensions.cs @@ -4,8 +4,6 @@ using Marten.Linq.SqlGeneration; using Marten.Testing.Documents; using Shouldly; -// OperationRole moved to Weasel.Core per the dedup audit (#4350 / pillar #214). -using OperationRole = Weasel.Core.OperationRole; namespace DocumentDbTests.SessionMechanics; diff --git a/src/EventSourcingTests/GlobalUsings.cs b/src/EventSourcingTests/GlobalUsings.cs deleted file mode 100644 index 4bcbb9088e..0000000000 --- a/src/EventSourcingTests/GlobalUsings.cs +++ /dev/null @@ -1,5 +0,0 @@ -global using IStorageOperation = Marten.Internal.Operations.IStorageOperation; -// OperationRole consolidated to Weasel.Core per the dedup audit (#4350 / pillar #214). -global using OperationRole = Weasel.Core.OperationRole; -// SnapshotLifecycle consolidated to JasperFx.Events per the dedup audit (jasperfx#220 / pillar #214). -global using SnapshotLifecycle = JasperFx.Events.Projections.SnapshotLifecycle; diff --git a/src/IssueService/GlobalUsings.cs b/src/IssueService/GlobalUsings.cs deleted file mode 100644 index 99003119ee..0000000000 --- a/src/IssueService/GlobalUsings.cs +++ /dev/null @@ -1,2 +0,0 @@ -// SnapshotLifecycle consolidated to JasperFx.Events per the dedup audit (jasperfx#220 / pillar #214). -global using SnapshotLifecycle = JasperFx.Events.Projections.SnapshotLifecycle; diff --git a/src/LinqTestsTypes/LinqTestsTypes.csproj b/src/LinqTestsTypes/LinqTestsTypes.csproj index 5ea5cd7bfb..f79887991c 100644 --- a/src/LinqTestsTypes/LinqTestsTypes.csproj +++ b/src/LinqTestsTypes/LinqTestsTypes.csproj @@ -4,6 +4,9 @@ net9.0;net10.0 enable enable + + false diff --git a/src/Marten/Events/Dcb/DcbConcurrencyException.cs b/src/Marten/Events/Dcb/DcbConcurrencyException.cs deleted file mode 100644 index 624103e3a3..0000000000 --- a/src/Marten/Events/Dcb/DcbConcurrencyException.cs +++ /dev/null @@ -1,22 +0,0 @@ -using System; -using JasperFx.Events.Tags; -using Marten.Exceptions; - -namespace Marten.Events.Dcb; - -/// -/// Thrown when a DCB consistency check fails — new events matching the tag query -/// were appended after the boundary was established. -/// -public class DcbConcurrencyException: MartenException -{ - public DcbConcurrencyException(EventTagQuery query, long lastSeenSequence) - : base($"DCB consistency violation: new events matching the tag query were appended after sequence {lastSeenSequence}") - { - Query = query; - LastSeenSequence = lastSeenSequence; - } - - public EventTagQuery Query { get; } - public long LastSeenSequence { get; } -} diff --git a/src/Marten/GlobalUsings.cs b/src/Marten/GlobalUsings.cs deleted file mode 100644 index f8efdde1fc..0000000000 --- a/src/Marten/GlobalUsings.cs +++ /dev/null @@ -1,32 +0,0 @@ -global using IStorageOperation = Marten.Internal.Operations.IStorageOperation; -// OperationRole consolidated to Weasel.Core per the dedup audit (#4350 / pillar #214). -// Marten previously declared its own byte-identical copy; this aliases all -// existing call sites to the canonical Weasel.Core enum without a code sweep. -global using OperationRole = Weasel.Core.OperationRole; -// SnapshotLifecycle consolidated to JasperFx.Events per the dedup audit -// (jasperfx#220 / pillar #214). Same pattern as OperationRole above. -global using SnapshotLifecycle = JasperFx.Events.Projections.SnapshotLifecycle; -// TenancyStyle + DeleteStyle consolidated to JasperFx per the dedup audit -// (jasperfx#327 / marten#4517 / pillar #214). Ordinals unchanged -// (Single=0/Conjoined=1, Remove=0/SoftDelete=1). Same alias pattern as above. -global using TenancyStyle = JasperFx.MultiTenancy.TenancyStyle; -global using DeleteStyle = JasperFx.DeleteStyle; -// Metadata markers consolidated to JasperFx.Metadata per the dedup audit -// (jasperfx#330 / marten#4520 / pillar #214). Shapes are byte-identical -// (ITracked uses Marten's non-nullable string). Same alias pattern as above. -global using ISoftDeleted = JasperFx.Metadata.ISoftDeleted; -global using IVersioned = JasperFx.Metadata.IVersioned; -global using ITracked = JasperFx.Metadata.ITracked; -// Patching surface consolidated to JasperFx.Events per the dedup audit -// (jasperfx#331 / marten#4521 / pillar #214). RemoveAction (enum) is aliased here; -// IPatchExpression is an open generic so it cannot be aliased — its consumer -// files import JasperFx.Events directly. -global using RemoveAction = JasperFx.Events.RemoveAction; -// IDocumentSchemaResolver consolidated to JasperFx.Events per the dedup audit -// (jasperfx#333 / marten#4523 / pillar #214). Contract is verbatim from Marten's; -// StoreOptions still implements it (explicit impls bind via this alias). -global using IDocumentSchemaResolver = JasperFx.Events.IDocumentSchemaResolver; -// TrackLevel consolidated to JasperFx.OpenTelemetry per the dedup audit -// (jasperfx#332 / marten#4522 / pillar #214). Marten.Services.OpenTelemetryOptions -// derives from the lifted base; TrackLevel is aliased here. -global using TrackLevel = JasperFx.OpenTelemetry.TrackLevel; diff --git a/src/Shared/DedupeAliases.cs b/src/Shared/DedupeAliases.cs new file mode 100644 index 0000000000..6be8059583 --- /dev/null +++ b/src/Shared/DedupeAliases.cs @@ -0,0 +1,38 @@ +// Centralized global-using aliases for the Critter Stack 2026 dedupe pillar +// (jasperfx#214). Marten previously declared its own copies of these types; as +// each is lifted into JasperFx / JasperFx.Events / Weasel.Core, Marten consumes +// the canonical type and aliases the old unqualified name here so existing call +// sites across Marten + its test/extension projects keep compiling without a +// per-file sweep. +// +// This file is linked into every JasperFx-referencing project via +// Directory.Build.props (ImplicitUsings is off in this repo, so MSBuild +// items don't apply — a linked C# global-using file is used instead). + +global using IStorageOperation = Marten.Internal.Operations.IStorageOperation; +// OperationRole -> Weasel.Core (#4350 / pillar #214) +global using OperationRole = Weasel.Core.OperationRole; +// SnapshotLifecycle -> JasperFx.Events (jasperfx#220 / pillar #214) +global using SnapshotLifecycle = JasperFx.Events.Projections.SnapshotLifecycle; +// TenancyStyle + DeleteStyle -> JasperFx (jasperfx#327 / marten#4517) +global using TenancyStyle = JasperFx.MultiTenancy.TenancyStyle; +global using DeleteStyle = JasperFx.DeleteStyle; +// Metadata markers -> JasperFx.Metadata (jasperfx#330 / marten#4520) +global using ISoftDeleted = JasperFx.Metadata.ISoftDeleted; +global using IVersioned = JasperFx.Metadata.IVersioned; +global using ITracked = JasperFx.Metadata.ITracked; +// Patching -> JasperFx.Events (jasperfx#331 / marten#4521); IPatchExpression +// is an open generic and cannot be aliased. +global using RemoveAction = JasperFx.Events.RemoveAction; +// IDocumentSchemaResolver -> JasperFx.Events (jasperfx#333 / marten#4523) +global using IDocumentSchemaResolver = JasperFx.Events.IDocumentSchemaResolver; +// TrackLevel -> JasperFx.OpenTelemetry (jasperfx#332 / marten#4522) +global using TrackLevel = JasperFx.OpenTelemetry.TrackLevel; +// DcbConcurrencyException -> JasperFx.Events (jasperfx#328 / marten#4518) +global using DcbConcurrencyException = JasperFx.Events.DcbConcurrencyException; +// IdentityAttribute -> JasperFx (jasperfx#335 / marten#4525). Was an empty +// MartenAttribute marker; only consumed via HasAttribute(). +global using IdentityAttribute = JasperFx.IdentityAttribute; +// Serialization enums -> Weasel.Core (weasel#287 / marten#4527). Members unchanged. +global using Casing = Weasel.Core.Casing; +global using NonPublicMembersStorage = Weasel.Core.NonPublicMembersStorage; diff --git a/src/StressTests/GlobalUsings.cs b/src/StressTests/GlobalUsings.cs deleted file mode 100644 index 99003119ee..0000000000 --- a/src/StressTests/GlobalUsings.cs +++ /dev/null @@ -1,2 +0,0 @@ -// SnapshotLifecycle consolidated to JasperFx.Events per the dedup audit (jasperfx#220 / pillar #214). -global using SnapshotLifecycle = JasperFx.Events.Projections.SnapshotLifecycle; diff --git a/src/samples/DocSamples/GlobalUsings.cs b/src/samples/DocSamples/GlobalUsings.cs index 877145b75a..d6f5affa35 100644 --- a/src/samples/DocSamples/GlobalUsings.cs +++ b/src/samples/DocSamples/GlobalUsings.cs @@ -1,3 +1,3 @@ global using Xunit; -// SnapshotLifecycle consolidated to JasperFx.Events per the dedup audit (jasperfx#220 / pillar #214). -global using SnapshotLifecycle = JasperFx.Events.Projections.SnapshotLifecycle; +// Dedupe-pillar aliases (SnapshotLifecycle, etc.) come from the shared +// src/Shared/DedupeAliases.cs linked via Directory.Build.props. From 88c3ac10ab712cfbfdd981cee67752008e82aacc Mon Sep 17 00:00:00 2001 From: "Jeremy D. Miller" Date: Wed, 20 May 2026 09:59:17 -0500 Subject: [PATCH 09/11] #4525: route ID resolution through JasperFx.DocumentIdentity + forward IdentityAttribute jasperfx#335 lifted JasperFx.IdentityAttribute + a side-effect-free JasperFx.DocumentIdentity.FindIdMember(Type, Func). Delete Marten's empty MartenAttribute marker IdentityAttribute and alias the old name; it was only consumed via HasAttribute() (the MartenAttribute pipeline called a no-op Modify on it, so dropping the base changes nothing). DocumentMapping.FindIdMember now delegates to the lifted helper, passing Marten's own IsValidIdentityType predicate (which also recognizes strong-typed ids + F# DUs). The lifted traversal + GetProperties are byte-identical to Marten's prior implementation, so ID resolution behavior is unchanged; the now- dead private GetProperties helper is removed. Fixed three tests that used the fully-qualified Marten.Storage.TenancyStyle (now aliased). Closes #4525. Co-Authored-By: Claude Opus 4.7 (1M context) --- ...hived_partitioning_with_strong_typed_id.cs | 2 +- .../custom_transformation_of_events.cs | 2 +- ..._should_enforce_that_it_is_a_new_stream.cs | 4 +-- src/Marten/Schema/DocumentMapping.cs | 31 ++++--------------- src/Marten/Schema/IdentityAttribute.cs | 13 -------- 5 files changed, 10 insertions(+), 42 deletions(-) delete mode 100644 src/Marten/Schema/IdentityAttribute.cs diff --git a/src/CoreTests/Bugs/Bug_4190_archived_partitioning_with_strong_typed_id.cs b/src/CoreTests/Bugs/Bug_4190_archived_partitioning_with_strong_typed_id.cs index 7e5b5ac867..6de8db27e1 100644 --- a/src/CoreTests/Bugs/Bug_4190_archived_partitioning_with_strong_typed_id.cs +++ b/src/CoreTests/Bugs/Bug_4190_archived_partitioning_with_strong_typed_id.cs @@ -89,7 +89,7 @@ public async Task can_create_schema_with_archived_partitioning_conjoined_tenancy StoreOptions(opts => { opts.Events.UseArchivedStreamPartitioning = true; - opts.Events.TenancyStyle = Marten.Storage.TenancyStyle.Conjoined; + opts.Events.TenancyStyle = TenancyStyle.Conjoined; opts.Events.RegisterTagType("entity"); }); diff --git a/src/EventSourcingTests/Projections/custom_transformation_of_events.cs b/src/EventSourcingTests/Projections/custom_transformation_of_events.cs index 459b077235..8008717919 100644 --- a/src/EventSourcingTests/Projections/custom_transformation_of_events.cs +++ b/src/EventSourcingTests/Projections/custom_transformation_of_events.cs @@ -39,7 +39,7 @@ public async Task updateonly_event_for_custom_view_projection_should_not_create_ StoreOptions(opts => { opts.AutoCreateSchemaObjects = AutoCreate.All; - opts.Events.TenancyStyle = Marten.Storage.TenancyStyle.Conjoined; + opts.Events.TenancyStyle = TenancyStyle.Conjoined; opts.Schema.For().MultiTenanted(); opts.Projections.Add(new NewsletterSubscriptionProjection(), ProjectionLifecycle.Inline); diff --git a/src/EventSourcingTests/start_stream_should_enforce_that_it_is_a_new_stream.cs b/src/EventSourcingTests/start_stream_should_enforce_that_it_is_a_new_stream.cs index 9351719a26..b624d706f7 100644 --- a/src/EventSourcingTests/start_stream_should_enforce_that_it_is_a_new_stream.cs +++ b/src/EventSourcingTests/start_stream_should_enforce_that_it_is_a_new_stream.cs @@ -57,7 +57,7 @@ await Should.ThrowAsync(async () => [Fact] public async Task does_not_throw_exception_if_start_stream_is_called_on_existing_stream_with_the_same_tenant_and_tenancy_style_conjoined() { - StoreOptions(_ => _.Events.TenancyStyle = Marten.Storage.TenancyStyle.Conjoined); + StoreOptions(_ => _.Events.TenancyStyle = TenancyStyle.Conjoined); var stream = Guid.NewGuid(); const string tenantName = "Tenant"; @@ -82,7 +82,7 @@ await Should.ThrowAsync(async () => [Fact] public async Task does_not_throw_exception_if_start_stream_is_called_on_existing_stream_with_different_tenant_and_tenancy_style_conjoined() { - StoreOptions(_ => _.Events.TenancyStyle = Marten.Storage.TenancyStyle.Conjoined); + StoreOptions(_ => _.Events.TenancyStyle = TenancyStyle.Conjoined); var stream = Guid.NewGuid(); diff --git a/src/Marten/Schema/DocumentMapping.cs b/src/Marten/Schema/DocumentMapping.cs index 6c1814d8f0..8b6e1d4a4d 100644 --- a/src/Marten/Schema/DocumentMapping.cs +++ b/src/Marten/Schema/DocumentMapping.cs @@ -457,31 +457,12 @@ public static DocumentMapping For(string databaseSchemaName = SchemaConsta public static MemberInfo FindIdMember(Type documentType) { - // Order of finding id member should be - // 1) IdentityAttribute on property - // 2) IdentityAttribute on field - // 3) Id Property - // 4) Id field - var propertiesWithTypeValidForId = GetProperties(documentType) - .Where(p => IsValidIdentityType(p.PropertyType)); - var fieldsWithTypeValidForId = documentType.GetFields() - .Where(f => IsValidIdentityType(f.FieldType)); - return propertiesWithTypeValidForId.FirstOrDefault(x => x.HasAttribute()) - ?? fieldsWithTypeValidForId.FirstOrDefault(x => x.HasAttribute()) - ?? (MemberInfo)propertiesWithTypeValidForId - .FirstOrDefault(x => x.Name.Equals("id", StringComparison.OrdinalIgnoreCase)) - ?? fieldsWithTypeValidForId - .FirstOrDefault(x => x.Name.Equals("id", StringComparison.OrdinalIgnoreCase)); - } - - private static PropertyInfo[] GetProperties(Type type) - { - return type.GetTypeInfo().IsInterface - ? new[] { type } - .Concat(type.GetInterfaces()) - .SelectMany(i => i.GetProperties()).ToArray() - : type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic) - .OrderByDescending(x => x.DeclaringType == type).ToArray(); + // #4525: the attribute-then-convention traversal (IdentityAttribute on a + // property, then a field, then a case-insensitive "id" property/field) is + // lifted verbatim to JasperFx.DocumentIdentity. Marten keeps its own + // IsValidIdentityType predicate (which also recognizes strong-typed ids and + // F# DUs) so resolution behavior is unchanged. + return JasperFx.DocumentIdentity.FindIdMember(documentType, IsValidIdentityType); } public DocumentIndex AddGinIndexToData() diff --git a/src/Marten/Schema/IdentityAttribute.cs b/src/Marten/Schema/IdentityAttribute.cs deleted file mode 100644 index eef1e15cec..0000000000 --- a/src/Marten/Schema/IdentityAttribute.cs +++ /dev/null @@ -1,13 +0,0 @@ -#nullable enable -using System; - -namespace Marten.Schema; - -/// -/// Use to designate an Id property or field on a document type that doesn't follow the -/// id/Id naming convention -/// -[AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)] -public class IdentityAttribute: MartenAttribute -{ -} From ce92778a986c5e6b0863f7803a3816d46b6e718d Mon Sep 17 00:00:00 2001 From: "Jeremy D. Miller" Date: Wed, 20 May 2026 09:59:26 -0500 Subject: [PATCH 10/11] #4527 (partial): consume lifted Weasel.Core serialization enums (Casing, NonPublicMembersStorage) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Weasel 9.0.0-alpha.8 lifted the Casing and NonPublicMembersStorage enums into Weasel.Core (weasel#287). Delete Marten's byte-identical copies from ISerializer.cs and alias the old names in the shared dedupe file. Members are unchanged (Default/CamelCase/SnakeCase; the [Flags] non-public storage set). This is the forced serialization-enum subset of #4527 (the bump won't compile without it). The Hi-Lo sequence base refactor (HiloSequenceBase) — the behavior-sensitive remainder of #4527 — is deferred to the follow-up PR with #4516 and #4526/#4528. Refs #4527. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/Marten/ISerializer.cs | 37 ++++--------------------------------- 1 file changed, 4 insertions(+), 33 deletions(-) diff --git a/src/Marten/ISerializer.cs b/src/Marten/ISerializer.cs index b5f57a6775..4c77486a9f 100644 --- a/src/Marten/ISerializer.cs +++ b/src/Marten/ISerializer.cs @@ -160,30 +160,8 @@ ValueTask FromJsonAsync(Type type, DbDataReader reader, int index, #endregion -/// -/// Governs the JSON serialization behavior of how .Net -/// member names are persisted in the JSON stored in -/// the database -/// -public enum Casing -{ - /// - /// Exactly mimic the .Net member names in the JSON persisted to the database - /// - Default, - - /// - /// Force the .Net member names to camel casing when serialized to JSON in - /// the database - /// - CamelCase, - - /// - /// Force the .Net member names to snake casing when serialized to JSON in - /// the database - /// - SnakeCase -} +// Casing consolidated to Weasel.Core per the dedup audit (marten#4527 / pillar #214); +// aliased in src/Shared/DedupeAliases.cs. /// /// Governs .Net collection serialization @@ -202,12 +180,5 @@ public enum CollectionStorage AsArray } -[Flags] -public enum NonPublicMembersStorage -{ - Default = 0, - NonPublicSetters = 1, - NonPublicDefaultConstructor = 2, - NonPublicConstructor = 4, - All = Default | NonPublicSetters | NonPublicDefaultConstructor | NonPublicConstructor -} +// NonPublicMembersStorage consolidated to Weasel.Core per the dedup audit +// (marten#4527 / pillar #214); aliased in src/Shared/DedupeAliases.cs. From 54b37fcf211071857ed556961d5653e4f5785664 Mon Sep 17 00:00:00 2001 From: "Jeremy D. Miller" Date: Wed, 20 May 2026 10:01:41 -0500 Subject: [PATCH 11/11] #4524: re-base IInitialData + IConfigureMarten(+async) on lifted JasperFx generics jasperfx#334 lifted IInitialData, IConfigureStore, and IAsyncConfigureStore to JasperFx core. Re-base Marten's interfaces on the generic contracts by inheritance (preserving the names and every existing implementer, rather than deleting): - Marten.Schema.IInitialData : JasperFx.IInitialData (Populate(IDocumentStore, CancellationToken) is the closed generic member; MartenActivator + existing seed-data impls unchanged). - IConfigureMarten : JasperFx.IConfigureStore (Configure(IServiceProvider, StoreOptions) inherited). - IAsyncConfigureMarten : JasperFx.IAsyncConfigureStore (Configure(StoreOptions, CancellationToken) inherited). - IConfigureMarten keeps its marker role (transitively : IConfigureStore). The AddMarten() factory's IConfigureMarten discovery binds the inherited Configure unchanged; full solution builds clean across net9/net10. Closes #4524. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/Marten/MartenServiceCollectionExtensions.cs | 6 ++---- src/Marten/Schema/IInitialData.cs | 15 ++++++--------- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/Marten/MartenServiceCollectionExtensions.cs b/src/Marten/MartenServiceCollectionExtensions.cs index 7b09cd0da7..5ea5f19284 100644 --- a/src/Marten/MartenServiceCollectionExtensions.cs +++ b/src/Marten/MartenServiceCollectionExtensions.cs @@ -973,9 +973,8 @@ public interface IGlobalConfigureMarten: IConfigureMarten; /// Mechanism to register additional Marten configuration that is applied after AddMarten() /// configuration, but before DocumentStore is initialized /// -public interface IConfigureMarten +public interface IConfigureMarten: JasperFx.IConfigureStore { - void Configure(IServiceProvider services, StoreOptions options); } #endregion @@ -987,9 +986,8 @@ public interface IConfigureMarten /// configuration, but before DocumentStore is initialized when you need to utilize some /// kind of asynchronous services like Microsoft's FeatureManagement feature to configure Marten /// -public interface IAsyncConfigureMarten +public interface IAsyncConfigureMarten: JasperFx.IAsyncConfigureStore { - ValueTask Configure(StoreOptions options, CancellationToken cancellationToken); } #endregion diff --git a/src/Marten/Schema/IInitialData.cs b/src/Marten/Schema/IInitialData.cs index fb9a6c0d54..6c7d2b6851 100644 --- a/src/Marten/Schema/IInitialData.cs +++ b/src/Marten/Schema/IInitialData.cs @@ -1,18 +1,15 @@ #nullable enable -using System.Threading; -using System.Threading.Tasks; namespace Marten.Schema; /// /// A set of initial data to pre-populate a DocumentStore at startup time -/// Users will have to be responsible for not duplicating data +/// Users will have to be responsible for not duplicating data. +/// Re-based on the lifted +/// (jasperfx#334 / marten#4524); the Populate(IDocumentStore, CancellationToken) +/// signature is unchanged so existing implementers and MartenActivator are +/// unaffected. /// -public interface IInitialData +public interface IInitialData: JasperFx.IInitialData { - /// - /// Apply the data loading - /// - /// - Task Populate(IDocumentStore store, CancellationToken cancellation); }