Goal
Allow consumers of JasperFx.Events to resolve JasperFx.Events.Daemon.IProjectionCoordinator directly from DI without needing to know which event store provider is hosting the application.
Current state
Both Marten and Polecat register their store-specific subtype, not the JasperFx base interface:
// Marten/Marten/MartenServiceCollectionExtensions.cs:755
Services.AddSingleton<Marten.Events.Daemon.Coordination.IProjectionCoordinator, ProjectionCoordinator>();
// Polecat/PolecatConfigurationExpression.cs:86
Services.AddSingleton<Polecat.Events.Daemon.Coordination.IProjectionCoordinator>(sp => ...);
Both store-specific interfaces inherit from JasperFx.Events.Daemon.IProjectionCoordinator, but the DI container only resolves the exact registered type. So:
services.GetService<JasperFx.Events.Daemon.IProjectionCoordinator>() // null on a stock Marten OR Polecat host
Impact
Wolverine.CritterWatch (the cross-store monitoring client) wants to resolve IProjectionCoordinator once and not branch on store. It currently has to walk every registered IHostedService and OfType<IProjectionCoordinator>() — works, but it's awkward and depends on the implementation detail that both coordinators are also registered as IHostedService.
See JasperFx/CritterWatch#310 and the regression test at https://github.com/JasperFx/CritterWatch/blob/main/src/Tests/Integration/subscription_rewind_polecat.cs that pins this exact gap (the test asserts the direct lookup is null on both stores).
Proposed fix
In both MartenServiceCollectionExtensions.AddAsyncDaemon and PolecatConfigurationExpression.AddProjectionCoordinator, add a second AddSingleton factory that delegates to the store-specific registration:
// Marten:
Services.AddSingleton<JasperFx.Events.Daemon.IProjectionCoordinator>(sp =>
sp.GetRequiredService<Marten.Events.Daemon.Coordination.IProjectionCoordinator>());
// Polecat:
Services.AddSingleton<JasperFx.Events.Daemon.IProjectionCoordinator>(sp =>
sp.GetRequiredService<Polecat.Events.Daemon.Coordination.IProjectionCoordinator>());
Singleton-factory-delegating preserves the single-instance invariant — both keys resolve to the same coordinator.
Once this lands across the Marten + Polecat versions CritterWatch pins, the Wolverine.CritterWatch handler can collapse the walk back to a single GetService<IProjectionCoordinator>() call. The repeating-gap regression test on the CritterWatch side will fail on the next bump and remind us to flip the handler.
Scope
Two-line change in each of Marten and Polecat. Coordinated release across the family (CritterWatch follow-up bumps the package pins and simplifies the handler).
Goal
Allow consumers of JasperFx.Events to resolve
JasperFx.Events.Daemon.IProjectionCoordinatordirectly from DI without needing to know which event store provider is hosting the application.Current state
Both Marten and Polecat register their store-specific subtype, not the JasperFx base interface:
Both store-specific interfaces inherit from
JasperFx.Events.Daemon.IProjectionCoordinator, but the DI container only resolves the exact registered type. So:Impact
Wolverine.CritterWatch(the cross-store monitoring client) wants to resolveIProjectionCoordinatoronce and not branch on store. It currently has to walk every registeredIHostedServiceandOfType<IProjectionCoordinator>()— works, but it's awkward and depends on the implementation detail that both coordinators are also registered asIHostedService.See JasperFx/CritterWatch#310 and the regression test at https://github.com/JasperFx/CritterWatch/blob/main/src/Tests/Integration/subscription_rewind_polecat.cs that pins this exact gap (the test asserts the direct lookup is null on both stores).
Proposed fix
In both
MartenServiceCollectionExtensions.AddAsyncDaemonandPolecatConfigurationExpression.AddProjectionCoordinator, add a secondAddSingletonfactory that delegates to the store-specific registration:Singleton-factory-delegating preserves the single-instance invariant — both keys resolve to the same coordinator.
Once this lands across the Marten + Polecat versions CritterWatch pins, the
Wolverine.CritterWatchhandler can collapse the walk back to a singleGetService<IProjectionCoordinator>()call. The repeating-gap regression test on the CritterWatch side will fail on the next bump and remind us to flip the handler.Scope
Two-line change in each of Marten and Polecat. Coordinated release across the family (CritterWatch follow-up bumps the package pins and simplifies the handler).