From dea300bb129b30e904f9e4cc57431da77c559c15 Mon Sep 17 00:00:00 2001 From: "Jeremy D. Miller" Date: Mon, 18 May 2026 09:28:03 -0500 Subject: [PATCH 1/4] [#4349] Add Marten.AotSmoke project + Static-mode bootstrap MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit New build-time consumer project that mirrors src/JasperFx.AotSmoke/ and src/Weasel.Core.AotSmoke/. Builds with IsAotCompatible=true, TrimMode=full, and the IL2026/IL2046/IL2055/IL2065/IL2067/IL2070/IL2072/ IL2075/IL2090/IL2091/IL2111/IL3050/IL3051 warnings-as-errors set — any change that adds [RequiresDynamicCode] / [RequiresUnreferencedCode] to a Marten API exercised here fails CI, and any change to Program.cs that calls into a reflective Marten surface fails too. Surface exercised: - services.AddMarten(opts => { ... }) — DI-facing entry point. - StoreOptions.GeneratedCodeMode = TypeLoadMode.Static — pins the consumer profile AOT-publishing apps would set. - Schema.For() — closed-shape IDocumentStorage path. - Events.AddEventType() — explicit alias registration. - Projections.Add(Inline) — SingleStreamProjection<,> registration (the JasperFx.Events.SourceGenerator [GeneratedEvolver] discovery surface). - Host.CreateApplicationBuilder + Build + IDocumentStore DI resolve. Regression guard verification (chip acceptance criterion): manually inserted `object obj = "x"; obj.GetType().GetMethod("ToString")` and confirmed it fails with `error IL2075` on both net9.0 and net10.0, then reverted. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/Marten.AotSmoke/Marten.AotSmoke.csproj | 45 +++++++++ src/Marten.AotSmoke/Program.cs | 111 +++++++++++++++++++++ src/Marten.slnx | 1 + 3 files changed, 157 insertions(+) create mode 100644 src/Marten.AotSmoke/Marten.AotSmoke.csproj create mode 100644 src/Marten.AotSmoke/Program.cs diff --git a/src/Marten.AotSmoke/Marten.AotSmoke.csproj b/src/Marten.AotSmoke/Marten.AotSmoke.csproj new file mode 100644 index 0000000000..fcd3133345 --- /dev/null +++ b/src/Marten.AotSmoke/Marten.AotSmoke.csproj @@ -0,0 +1,45 @@ + + + + Exe + enable + enable + false + + + true + full + IL2026;IL2046;IL2055;IL2065;IL2067;IL2070;IL2072;IL2075;IL2090;IL2091;IL2111;IL3050;IL3051 + + + + + + + diff --git a/src/Marten.AotSmoke/Program.cs b/src/Marten.AotSmoke/Program.cs new file mode 100644 index 0000000000..9fc613dee3 --- /dev/null +++ b/src/Marten.AotSmoke/Program.cs @@ -0,0 +1,111 @@ +// AOT smoke test (marten#4349 / JasperFx/jasperfx#213). +// +// This program touches a representative cross-section of the AOT-clean +// Marten surface for a Static-TypeLoadMode consumer. The csproj sets +// IsAotCompatible=true, TrimMode=full, and promotes the AOT analyzer warning +// codes to errors, so any change that adds [RequiresDynamicCode] / +// [RequiresUnreferencedCode] to an API exercised here — or any change to +// this file that calls into a reflective Marten surface — fails the build +// in CI. +// +// What's exercised: +// - services.AddMarten(opts => { ... }) — the DI-facing entry point. +// - One document type with an Id property — exercises the closed-shape +// IDocumentStorage / IIdentification path (post-#4404, no Roslyn emit). +// - One event type + one SingleStreamProjection<,> registration — the +// primary projection-apply surface that Marten 9 routes through +// JasperFx.Events.SourceGenerator's [GeneratedEvolver] discovery +// (Options.Projections.DiscoverGeneratedEvolvers is called at startup +// in DocumentStore.cs). +// - StoreOptions.GeneratedCodeMode = TypeLoadMode.Static — pins the +// consumer profile that AOT-publishing apps would set. +// - Host.CreateApplicationBuilder + Build + DI resolution of +// IDocumentStore — the boot path consumers actually hit. +// +// What's deliberately NOT exercised: +// - Connecting / querying / persisting anything — this is a build-time +// analyzer test, not a runtime test. The connection string is a +// placeholder; the host is built but never actually opens a connection. +// - Compiled queries — Marten.SourceGenerator covers that surface and has +// its own analyzer-level tests in Marten.SourceGenerator.Tests. +// - JasperFx.RuntimeCompiler / services.AddRuntimeCompilation() — Marten 9 +// retired that path (#4454 / #4461). This smoke deliberately does not +// pull it back in. + +using JasperFx.CodeGeneration; +using JasperFx.Events; +using JasperFx.Events.Projections; +using Marten; +using Marten.Events.Aggregation; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +const string placeholderConnectionString = + "Host=localhost;Port=5432;Database=marten_aot_smoke;Username=postgres;password=postgres"; + +var builder = Host.CreateApplicationBuilder(); + +builder.Services.AddMarten(opts => +{ + opts.Connection(placeholderConnectionString); + + // Pin the consumer profile an AOT-publishing app would set. Marten 9 + // retired the runtime-codegen path entirely, so this setting is a + // documented no-op kept for source-compatibility — exercising it here + // pins that compile-time contract. + opts.GeneratedCodeMode = TypeLoadMode.Static; + + // Exercise the document-mapping surface — registers the closed-shape + // IDocumentStorage path (#4404). DocumentMapping infers the Id member + // and the Id type from SmokeDoc.Id reflectively at registration time, + // which is part of Marten's [DynamicallyAccessedMembers]-annotated + // contract; the AOT analyzer is happy when the type is fed as + // typeof(T) literal as it is here. + opts.Schema.For(); + + // Exercise the event-registration surface — RegisterEventType is the + // explicit alias-registration path the JasperFx.Events source generator + // also feeds at module-init time. Done explicitly here so the smoke + // test doesn't rely on assembly scanning. + opts.Events.AddEventType(); + + // Exercise the projection-registration surface — Projections.Add + // closes over the user's SingleStreamProjection<,> type, which the + // JasperFx.Events.SourceGenerator handles via [GeneratedEvolver] + // discovery (DocumentStore.cs calls DiscoverGeneratedEvolvers at + // startup post-#4454). Inline lifecycle keeps the smoke test free of + // the async-daemon surface, which has its own AOT story. + opts.Projections.Add(ProjectionLifecycle.Inline); +}); + +using var host = builder.Build(); +var store = host.Services.GetRequiredService(); + +if (store is null) +{ + Console.Error.WriteLine("DI resolved a null IDocumentStore — regression in AddMarten."); + return 1; +} + +Console.WriteLine("Marten AOT smoke OK — AddMarten + Schema.For + AddEventType + Projections.Add + DI resolve clean."); +return 0; + + +public sealed class SmokeDoc +{ + public Guid Id { get; set; } + public string Name { get; set; } = string.Empty; + public int Count { get; set; } +} + +public sealed record SmokeEvent(int Delta); + +public sealed class SmokeProjection : SingleStreamProjection +{ + public override SmokeDoc Evolve(SmokeDoc? snapshot, Guid id, IEvent e) + { + snapshot ??= new SmokeDoc { Id = id }; + if (e.Data is SmokeEvent inc) snapshot.Count += inc.Delta; + return snapshot; + } +} diff --git a/src/Marten.slnx b/src/Marten.slnx index bcf234bc13..fd57e4b841 100644 --- a/src/Marten.slnx +++ b/src/Marten.slnx @@ -67,6 +67,7 @@ + From 80d009c46ccd969fee6be0ea8292a8b6e1756855 Mon Sep 17 00:00:00 2001 From: "Jeremy D. Miller" Date: Mon, 18 May 2026 09:28:12 -0500 Subject: [PATCH 2/4] [#4349] Flag-on IsAotCompatible for Marten.AspNetCore and Marten.NodaTime MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both packages compile clean with IsAotCompatible=true — no IL2026 / IL2070 / IL2075 / IL3050 warnings surface. AspNetCore's streaming endpoints route through Marten's already-annotated IDocumentStore / LINQ / JSON streaming paths; NodaTime is value-object types + npgsql type handlers, all AOT-friendly upstream. Matches the chip's expected per-package state for the two extensions with no AOT-hostile transitive surface. Co-Authored-By: Claude Opus 4.7 (1M context) --- src/Marten.AspNetCore/Marten.AspNetCore.csproj | 1 + src/Marten.NodaTime/Marten.NodaTime.csproj | 1 + 2 files changed, 2 insertions(+) diff --git a/src/Marten.AspNetCore/Marten.AspNetCore.csproj b/src/Marten.AspNetCore/Marten.AspNetCore.csproj index 9163cac737..c324b9e2b5 100644 --- a/src/Marten.AspNetCore/Marten.AspNetCore.csproj +++ b/src/Marten.AspNetCore/Marten.AspNetCore.csproj @@ -11,6 +11,7 @@ true true enable + true diff --git a/src/Marten.NodaTime/Marten.NodaTime.csproj b/src/Marten.NodaTime/Marten.NodaTime.csproj index 1e78118d7b..30c97aa796 100644 --- a/src/Marten.NodaTime/Marten.NodaTime.csproj +++ b/src/Marten.NodaTime/Marten.NodaTime.csproj @@ -11,6 +11,7 @@ true enable Marten.NodaTimePlugin + true From d372de3782879e07a23074af7c5789ff83026946 Mon Sep 17 00:00:00 2001 From: "Jeremy D. Miller" Date: Mon, 18 May 2026 09:28:25 -0500 Subject: [PATCH 3/4] [#4349] Marten.EntityFrameworkCore: IsAotCompatible=true with DAM-annotated wrapper MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit EF Core's reflective query path (IModel.FindEntityType / DbContext.Find / FindAsync / Activator.CreateInstance for DbContextOptions) needs [DynamicallyAccessedMembers] on the TDoc / TDbContext generic parameters the wrapper closes over. Without the annotation, the trim analyzer flags every call site with IL2087 / IL2091. Two DAM patterns applied at the wrapper: - TDoc (the EF entity type) — full DAM flag set (PublicConstructors | NonPublicConstructors | PublicFields | NonPublicFields | PublicProperties | NonPublicProperties | Interfaces) matching what IModel.FindEntityType and Find / FindAsync require. - TDbContext — PublicConstructors only, matching what Activator.CreateInstance(typeof(TDbContext), DbContextOptions) needs to find the (DbContextOptions) constructor. Annotated: - EfCoreProjectionStorage - EfCoreSingleStreamProjection - EfCoreMultiStreamProjection - EfCoreEventProjection - EfCoreDbContextFactory.Create(...) - EfCoreProjectionExtensions.AddEntityTablesFromDbContext(...) - EfCoreProjectionExtensions.Add(...) (all four overloads) The trim/AOT requirement propagates to consumers: callers close the generic parameters with concrete entity types and DbContext subclasses, satisfying the DAM constraint implicitly. After the annotation pass: Marten.EntityFrameworkCore builds with IsAotCompatible=true and 0 IL warnings. Marten.EntityFrameworkCore.Tests all 39 pass unchanged. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../EfCoreDbContextFactory.cs | 5 +- .../EfCoreEventProjection.cs | 5 +- .../EfCoreMultiStreamProjection.cs | 19 ++++++- .../EfCoreProjectionExtensions.cs | 53 +++++++++++++++++-- .../EfCoreProjectionStorage.cs | 18 ++++++- .../EfCoreSingleStreamProjection.cs | 19 ++++++- .../Marten.EntityFrameworkCore.csproj | 1 + 7 files changed, 110 insertions(+), 10 deletions(-) diff --git a/src/Marten.EntityFrameworkCore/EfCoreDbContextFactory.cs b/src/Marten.EntityFrameworkCore/EfCoreDbContextFactory.cs index 6fe31ee706..ceed738730 100644 --- a/src/Marten.EntityFrameworkCore/EfCoreDbContextFactory.cs +++ b/src/Marten.EntityFrameworkCore/EfCoreDbContextFactory.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using Microsoft.EntityFrameworkCore; using Npgsql; @@ -13,7 +14,9 @@ namespace Marten.EntityFrameworkCore; /// internal static class EfCoreDbContextFactory { - public static (TDbContext DbContext, NpgsqlConnection InitialConnection) Create( + public static (TDbContext DbContext, NpgsqlConnection InitialConnection) Create< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] + TDbContext>( this Storage.IMartenDatabase database, Action>? configure = null, string? schemaName = null) diff --git a/src/Marten.EntityFrameworkCore/EfCoreEventProjection.cs b/src/Marten.EntityFrameworkCore/EfCoreEventProjection.cs index 17995b0f20..c41e4cdcdf 100644 --- a/src/Marten.EntityFrameworkCore/EfCoreEventProjection.cs +++ b/src/Marten.EntityFrameworkCore/EfCoreEventProjection.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; using JasperFx.Events; @@ -15,7 +16,9 @@ namespace Marten.EntityFrameworkCore; /// in the same database transaction. /// /// The EF Core DbContext type to use -public abstract class EfCoreEventProjection: IProjection +public abstract class EfCoreEventProjection< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] + TDbContext>: IProjection where TDbContext : DbContext { /// diff --git a/src/Marten.EntityFrameworkCore/EfCoreMultiStreamProjection.cs b/src/Marten.EntityFrameworkCore/EfCoreMultiStreamProjection.cs index 8109641dc8..223dfcc607 100644 --- a/src/Marten.EntityFrameworkCore/EfCoreMultiStreamProjection.cs +++ b/src/Marten.EntityFrameworkCore/EfCoreMultiStreamProjection.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; using JasperFx.Core; @@ -23,7 +24,23 @@ namespace Marten.EntityFrameworkCore; /// The aggregate document type persisted by EF Core /// The aggregate identity type /// The EF Core DbContext type to use -public abstract class EfCoreMultiStreamProjection +/// +/// AOT: TDoc carries the DAM flag set required by the wrapped +/// — propagating +/// the EF Core FindEntityType / Find<TEntity> trim contract +/// up to consumers. +/// +public abstract class EfCoreMultiStreamProjection< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors + | DynamicallyAccessedMemberTypes.NonPublicConstructors + | DynamicallyAccessedMemberTypes.PublicFields + | DynamicallyAccessedMemberTypes.NonPublicFields + | DynamicallyAccessedMemberTypes.PublicProperties + | DynamicallyAccessedMemberTypes.NonPublicProperties + | DynamicallyAccessedMemberTypes.Interfaces)] + TDoc, TId, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] + TDbContext> : MultiStreamProjection, IValidatedProjection where TDoc : class where TId : notnull where TDbContext : DbContext { diff --git a/src/Marten.EntityFrameworkCore/EfCoreProjectionExtensions.cs b/src/Marten.EntityFrameworkCore/EfCoreProjectionExtensions.cs index 13a7ad15ff..abf33bf0d7 100644 --- a/src/Marten.EntityFrameworkCore/EfCoreProjectionExtensions.cs +++ b/src/Marten.EntityFrameworkCore/EfCoreProjectionExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Diagnostics.CodeAnalysis; using System.Linq; using JasperFx.Events.Projections; using Marten.Events.Projections; @@ -20,7 +21,17 @@ public static class EfCoreProjectionExtensions /// Automatically sets up EF Core-based aggregate persistence and Weasel schema migration /// for all entity types in the DbContext. /// - public static void Add(this StoreOptions options, + public static void Add< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors + | DynamicallyAccessedMemberTypes.NonPublicConstructors + | DynamicallyAccessedMemberTypes.PublicFields + | DynamicallyAccessedMemberTypes.NonPublicFields + | DynamicallyAccessedMemberTypes.PublicProperties + | DynamicallyAccessedMemberTypes.NonPublicProperties + | DynamicallyAccessedMemberTypes.Interfaces)] + TDoc, TId, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] + TDbContext>(this StoreOptions options, EfCoreSingleStreamProjection projection, ProjectionLifecycle lifecycle) where TDoc : class where TId : notnull where TDbContext : DbContext @@ -35,7 +46,17 @@ public static void Add(this StoreOptions options, /// Automatically sets up EF Core-based aggregate persistence and Weasel schema migration /// for all entity types in the DbContext. /// - public static void Add(this StoreOptions options, + public static void Add< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors + | DynamicallyAccessedMemberTypes.NonPublicConstructors + | DynamicallyAccessedMemberTypes.PublicFields + | DynamicallyAccessedMemberTypes.NonPublicFields + | DynamicallyAccessedMemberTypes.PublicProperties + | DynamicallyAccessedMemberTypes.NonPublicProperties + | DynamicallyAccessedMemberTypes.Interfaces)] + TDoc, TId, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] + TDbContext>(this StoreOptions options, EfCoreMultiStreamProjection projection, ProjectionLifecycle lifecycle) where TDoc : class where TId : notnull where TDbContext : DbContext @@ -49,7 +70,17 @@ public static void Add(this StoreOptions options, /// Add an to a composite projection. /// Registers EF Core-based aggregate persistence and Weasel schema migration. /// - public static void Add(this CompositeProjection composite, + public static void Add< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors + | DynamicallyAccessedMemberTypes.NonPublicConstructors + | DynamicallyAccessedMemberTypes.PublicFields + | DynamicallyAccessedMemberTypes.NonPublicFields + | DynamicallyAccessedMemberTypes.PublicProperties + | DynamicallyAccessedMemberTypes.NonPublicProperties + | DynamicallyAccessedMemberTypes.Interfaces)] + TDoc, TId, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] + TDbContext>(this CompositeProjection composite, StoreOptions options, EfCoreMultiStreamProjection projection, int stageNumber = 1) @@ -64,7 +95,17 @@ public static void Add(this CompositeProjection composite /// Add an to a composite projection. /// Registers EF Core-based aggregate persistence and Weasel schema migration. /// - public static void Add(this CompositeProjection composite, + public static void Add< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors + | DynamicallyAccessedMemberTypes.NonPublicConstructors + | DynamicallyAccessedMemberTypes.PublicFields + | DynamicallyAccessedMemberTypes.NonPublicFields + | DynamicallyAccessedMemberTypes.PublicProperties + | DynamicallyAccessedMemberTypes.NonPublicProperties + | DynamicallyAccessedMemberTypes.Interfaces)] + TDoc, TId, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] + TDbContext>(this CompositeProjection composite, StoreOptions options, EfCoreSingleStreamProjection projection, int stageNumber = 1) @@ -80,7 +121,9 @@ public static void Add(this CompositeProjection composite /// Weasel migration pipeline. Tables defined in the DbContext's model will be created /// and migrated automatically alongside Marten's own schema objects. /// - public static void AddEntityTablesFromDbContext(this StoreOptions options, + public static void AddEntityTablesFromDbContext< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] + TDbContext>(this StoreOptions options, Action>? configure = null) where TDbContext : DbContext { diff --git a/src/Marten.EntityFrameworkCore/EfCoreProjectionStorage.cs b/src/Marten.EntityFrameworkCore/EfCoreProjectionStorage.cs index 112ddf100d..91f98e2df6 100644 --- a/src/Marten.EntityFrameworkCore/EfCoreProjectionStorage.cs +++ b/src/Marten.EntityFrameworkCore/EfCoreProjectionStorage.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; using JasperFx.Events; @@ -17,7 +18,22 @@ namespace Marten.EntityFrameworkCore; /// /// swaps to Marten's connection and calls SaveChangesAsync. /// -internal class EfCoreProjectionStorage : IProjectionStorage +/// +/// AOT: TDoc carries the full DAM flag set required by +/// DbContext.Find<TEntity> / FindAsync<TEntity> and +/// IModel.FindEntityType(Type) on the EF Core API surface. The trim +/// requirement propagates to consumers — callers that close TDoc with a +/// concrete entity type satisfy it implicitly. +/// +internal class EfCoreProjectionStorage< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors + | DynamicallyAccessedMemberTypes.NonPublicConstructors + | DynamicallyAccessedMemberTypes.PublicFields + | DynamicallyAccessedMemberTypes.NonPublicFields + | DynamicallyAccessedMemberTypes.PublicProperties + | DynamicallyAccessedMemberTypes.NonPublicProperties + | DynamicallyAccessedMemberTypes.Interfaces)] + TDoc, TId, TDbContext> : IProjectionStorage where TDoc : class where TId : notnull where TDbContext : DbContext { public TDbContext DbContext { get; } diff --git a/src/Marten.EntityFrameworkCore/EfCoreSingleStreamProjection.cs b/src/Marten.EntityFrameworkCore/EfCoreSingleStreamProjection.cs index f61dc29604..6ed38d574a 100644 --- a/src/Marten.EntityFrameworkCore/EfCoreSingleStreamProjection.cs +++ b/src/Marten.EntityFrameworkCore/EfCoreSingleStreamProjection.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Threading; using System.Threading.Tasks; using JasperFx.Core; @@ -23,7 +24,23 @@ namespace Marten.EntityFrameworkCore; /// The aggregate document type persisted by EF Core /// The stream identity type /// The EF Core DbContext type to use -public abstract class EfCoreSingleStreamProjection +/// +/// AOT: TDoc carries the DAM flag set required by the wrapped +/// — propagating +/// the EF Core FindEntityType / Find<TEntity> trim contract +/// up to consumers. +/// +public abstract class EfCoreSingleStreamProjection< + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors + | DynamicallyAccessedMemberTypes.NonPublicConstructors + | DynamicallyAccessedMemberTypes.PublicFields + | DynamicallyAccessedMemberTypes.NonPublicFields + | DynamicallyAccessedMemberTypes.PublicProperties + | DynamicallyAccessedMemberTypes.NonPublicProperties + | DynamicallyAccessedMemberTypes.Interfaces)] + TDoc, TId, + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] + TDbContext> : SingleStreamProjection, IValidatedProjection where TDoc : class where TId : notnull where TDbContext : DbContext { diff --git a/src/Marten.EntityFrameworkCore/Marten.EntityFrameworkCore.csproj b/src/Marten.EntityFrameworkCore/Marten.EntityFrameworkCore.csproj index 154bbec59d..e391082779 100644 --- a/src/Marten.EntityFrameworkCore/Marten.EntityFrameworkCore.csproj +++ b/src/Marten.EntityFrameworkCore/Marten.EntityFrameworkCore.csproj @@ -11,6 +11,7 @@ true true enable + true From 42dc5f14d01da166b4d8c1d9e6d1d4a1497db00f Mon Sep 17 00:00:00 2001 From: "Jeremy D. Miller" Date: Mon, 18 May 2026 09:28:37 -0500 Subject: [PATCH 4/4] [#4349] Marten.Newtonsoft: document AOT opt-out (upstream Newtonsoft.Json is AOT-hostile) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Setting IsAotCompatible=true surfaces 4 IL2070 warnings on JsonNetObjectContractProvider.GetAttributeConstructor and GetTheMostSpecificConstructor — both methods receive their Type argument from Newtonsoft.Json's IContractResolver callback, and that Type comes unannotated from Newtonsoft itself. We can't satisfy the analyzer with wrapper-side DAM annotations because Newtonsoft.Json's IContractResolver contract doesn't carry them at the boundary. Newtonsoft.Json as a whole has no AOT story — its serializer reflects over the full member surface of every type it serializes without DAM annotations anywhere. Annotating the Marten wrapper papers over a small part of an inherently AOT-incompatible upstream. Per the chip's "AOT-hostile through and through" outcome: flag stays OFF on Marten.Newtonsoft. AOT consumers use UseSystemTextJsonForSerialization (Marten core) instead. A `` comment block in the csproj documents the decision so future maintainers don't re-flip the flag without re-reading the analysis. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../Marten.Newtonsoft.csproj | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/Marten.Newtonsoft/Marten.Newtonsoft.csproj b/src/Marten.Newtonsoft/Marten.Newtonsoft.csproj index 4b82d6e61d..b23361cd6b 100644 --- a/src/Marten.Newtonsoft/Marten.Newtonsoft.csproj +++ b/src/Marten.Newtonsoft/Marten.Newtonsoft.csproj @@ -11,6 +11,33 @@ true true enable + +