You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
AuditEntryProjection.Create and ReadAuditEntryProjection.Create in Encina.Audit.Marten accept IServiceProvider as their second parameter. Marten/JasperFx projection validation rejects this type at store initialization, so ConfigureMartenAuditProjections.Configure(StoreOptions)throws before the store can be used.
As a result, any application calling services.AddEncinaAuditMarten(...) will fail at boot. This bug has gone undetected because Encina.Audit.Marten has no integration tests (no tests/Encina.IntegrationTests/AuditMarten/ folder exists) — only unit tests that call projection.Create(evt, services) directly, bypassing Marten's projection graph validation.
Steps to Reproduce
Install Encina.Audit.Marten in any app with a real PostgreSQL connection.
// Minimal reproduction without Marten store bootusingEncina.Audit.Marten.Projections;usingMarten;varsut=newConfigureMartenAuditProjections();varstoreOptions=newStoreOptions();storeOptions.Connection("Host=localhost;Port=5432;Database=test;Username=postgres;Password=postgres");// Throws InvalidProjectionException on Projections.Add(...)sut.Configure(storeOptions);
publicasyncTask<AuditEntryReadModel>Create(AuditEntryRecordedEvent@event,IServiceProviderservices)// ← Marten rejects this parameter{varkeyProvider=services.GetRequiredService<ITemporalKeyProvider>();// ...}
Same shape in src/Encina.Audit.Marten/Projections/ReadAuditEntryProjection.cs.
Stack Trace
at JasperFx.Events.Projections.EventProjectionApplication`1.AssertMethodValidity()
at JasperFx.Events.Projections.JasperFxEventProjectionBase`2.AssembleAndAssertValidity()
at JasperFx.Events.Projections.ProjectionGraph`3.Register(...)
at JasperFx.Events.Projections.ProjectionGraph`3.Add(...)
at Encina.Audit.Marten.Projections.ConfigureMartenAuditProjections.Configure(StoreOptions options)
in src/Encina.Audit.Marten/Projections/ConfigureMartenAuditProjections.cs:line 40
Root Cause
The Marten projection pipeline injects per-event services through IDocumentOperations, not IServiceProvider. Temporal key providers and options must be resolved via a projection-scoped pattern (custom factory + constructor injection), not by taking the raw DI container as a method parameter.
Proposed Fix
Refactor both projections to one of the supported Marten patterns:
Preferred — Custom projection factory via IProjectionSource: resolve ITemporalKeyProvider, IOptions<MartenAuditOptions>, and the logger via constructor injection, and register the projection with a factory that pulls them from DI at store boot.
Alternative — IDocumentOperations + session-scoped service lookup: query temporal keys through Marten-managed sessions rather than a raw IServiceProvider.
Add an integration test under tests/Encina.IntegrationTests/AuditMarten/ that boots a real Marten store (Testcontainers PostgreSQL) and exercises AddEncinaAuditMarten() end-to-end. This test would have caught the bug on introduction.
Out of scope for this issue but recommended follow-up: audit all Encina.Marten / Encina.Marten.GDPR projections for the same anti-pattern.
Discovered while adding unit/guard coverage for Encina.Audit.Marten in PR test(audit-marten): close UNIT and GUARD coverage gaps #948. The Configure_ValidOptions_RegistersProjectionsAndIndexes test had to be removed; only the null-guard path remains testable at unit level.
No Encina.IntegrationTests/AuditMarten/ folder exists, so the Marten store has never been booted with these projections in CI. Any production app consuming the package would fail immediately.
Description
AuditEntryProjection.CreateandReadAuditEntryProjection.CreateinEncina.Audit.MartenacceptIServiceProvideras their second parameter. Marten/JasperFx projection validation rejects this type at store initialization, soConfigureMartenAuditProjections.Configure(StoreOptions)throws before the store can be used.As a result, any application calling
services.AddEncinaAuditMarten(...)will fail at boot. This bug has gone undetected becauseEncina.Audit.Martenhas no integration tests (notests/Encina.IntegrationTests/AuditMarten/folder exists) — only unit tests that callprojection.Create(evt, services)directly, bypassing Marten's projection graph validation.Steps to Reproduce
Encina.Audit.Martenin any app with a real PostgreSQL connection.ConfigureMartenAuditProjections.Configure(StoreOptions).JasperFx.Events.Projections.InvalidProjectionExceptionis thrown.Expected Behavior
Marten store initialization completes successfully and both audit projections are registered as async projections on the async daemon.
Actual Behavior
Environment
main, currentHEAD)Encina.Audit.Marten(AuditEntryProjection,ReadAuditEntryProjection,ConfigureMartenAuditProjections)Code Sample
Source (
src/Encina.Audit.Marten/Projections/AuditEntryProjection.cs:60-62):Same shape in
src/Encina.Audit.Marten/Projections/ReadAuditEntryProjection.cs.Stack Trace
Root Cause
The Marten projection pipeline injects per-event services through
IDocumentOperations, notIServiceProvider. Temporal key providers and options must be resolved via a projection-scoped pattern (custom factory + constructor injection), not by taking the raw DI container as a method parameter.Proposed Fix
Refactor both projections to one of the supported Marten patterns:
IProjectionSource: resolveITemporalKeyProvider,IOptions<MartenAuditOptions>, and the logger via constructor injection, and register the projection with a factory that pulls them from DI at store boot.IDocumentOperations+ session-scoped service lookup: query temporal keys throughMarten-managed sessions rather than a rawIServiceProvider.tests/Encina.IntegrationTests/AuditMarten/that boots a real Marten store (Testcontainers PostgreSQL) and exercisesAddEncinaAuditMarten()end-to-end. This test would have caught the bug on introduction.Out of scope for this issue but recommended follow-up: audit all Encina.Marten / Encina.Marten.GDPR projections for the same anti-pattern.
Related
[FEATURE] Add Encina.Audit.Marten)AggregateStreamAsync does not set Version) — both discovered by the integration gapCreatedirectly with a substituteIServiceProvider) added in PR test(audit-marten): close UNIT and GUARD coverage gaps #948Additional Context
Encina.Audit.Martenin PR test(audit-marten): close UNIT and GUARD coverage gaps #948. TheConfigure_ValidOptions_RegistersProjectionsAndIndexestest had to be removed; only the null-guard path remains testable at unit level.Encina.IntegrationTests/AuditMarten/folder exists, so the Marten store has never been booted with these projections in CI. Any production app consuming the package would fail immediately.