Skip to content

Bump Marten and 5 others - #212

Closed
dependabot[bot] wants to merge 1 commit into
mainfrom
dependabot/nuget/nuget-minor-patch-f8902eea98
Closed

dependabot[bot] wants to merge 1 commit into
mainfrom
dependabot/nuget/nuget-minor-patch-f8902eea98

Conversation

@dependabot

@dependabot dependabot Bot commented on behalf of github Sep 12, 2026 •

Copy link
Copy Markdown
Contributor

Pinned Marten at 9.33.0.

Release notes

Sourced from Marten's releases.

9.33.0

The concurrency and portability release: a shared-state race that broke parallel db-apply, a Native AOT query failure, and two cross-store divergences settled in Marten's favour and against it respectively.

Dependencies move to JasperFx 2.67.0 and Weasel 9.31.1.

db-apply --parallel could die with "Collection was modified" (#​5364)

Applying schema changes to many databases concurrently intermittently failed on one of them with:

System.InvalidOperationException: Collection was modified; enumeration operation may not execute.
  at Marten.Events.Schema.PerTenantEventSequences.currentPartitionSuffixes()
  at Weasel.Core.Migrations.DatabaseBase`1.assertValidIdentifiers(IEnumerable<ISchemaObject>)

Reported from a production deployment at 29 databases and --parallel 16, where it hit exactly one database per run.

One ManagedListPartitions instance is shared by every database in a store. It mutated its partition dictionary in place and published it through a ReadOnlyDictionary — which wraps rather than copies — so InitializeAsync's Clear()-and-refill ran while another database's worker was mid-enumeration. Fixed upstream in Weasel 9.31.1 (JasperFx/weasel#​583) by publishing the registry as a snapshot swapped copy-on-write. Marten needs only the bump; there is no Marten code change.

Two things worth knowing if you hit this before upgrading:

  • Downgrading does not help. This was reported as a 9.31.2 → 9.32.1 regression, and it is not one. There is no code change behind it in either Marten or Weasel across that range; the race has been latent since Marten's multi-database partition work and 9.32.1 only shifted the timing. Rolling back buys better odds, not a fix.
  • Retrying the failed command is safe. The throw happens before the apply opens a connection, so a failed database is left untouched rather than half-migrated.

The same Weasel fix covers Weasel.SqlServer's ManagedTenantPartitions, which had the identical shape.

An enum compared to a captured variable failed under Native AOT (#​5361)

where x.Status == captured threw BadLinqExpressionException wrapping PlatformNotSupportedException: Dynamic code generation is not supported on this platform, while the same comparison against a literal worked. C# lowers enum equality to a comparison of the underlying integers, so the value side always arrives as Convert(closure.captured, Int32) — a shape 9.32.1's reflective walk did not cover, which sent it to FastExpressionCompiler and therefore to Reflection.Emit.

Two changes, because the enum shape was the symptom rather than the cause. The reflective walk now handles conversions between an enum and anything sharing its integral representation, including the Nullable<T> forms — widening and narrowing are still left alone, so no value is ever reinterpreted. And ReduceToConstant no longer requires the ability to emit at all: where the platform cannot, shapes the walk declines fall back to the BCL expression interpreter instead of throwing. Without that second half, every expression shape Marten has not explicitly been taught was a latent AOT failure.

EventQuery.TagValues is now honored (#​5365)

The lossy name/value tag filter, on the composable query object. Previously you filtered by tags or by everything else: the dictionary form existed only on the non-composable QueryByTagsAsync, which is unpaged and has no TotalCount. Now a caller holding a tag name can AND it with the event type, window, stream, metadata and tenant filters and keep paging and a truthful total.

Note the semantics, which differ from the rich TagConditions form deliberately: entries AND (an event must carry every named tag at the given value), where TagConditions conditions OR. Supplying both spellings on one query is refused rather than resolved by a silent precedence rule.

Behaviour change: dictionary tag queries accept either spelling, case-insensitively

Fixing the above exposed a divergence in the existing IEventStore.QueryByTagsAsync(IReadOnlyDictionary<string,string>, …) overload. It matched a tag name against the CLR type name only and compared the value case-sensitively, while Polecat accepted either spelling and compared case-insensitively — so the same dictionary answered differently depending on which store you asked, and guessing wrong was an ArgumentException at runtime.

Both that overload and the new TagValues path now resolve names through the shared matcher (CLR simple name or registered table suffix, case-insensitively) and compare values against the string form of the stored tag value, ordinal case-insensitively. Postgres renders a Guid lowercase through ::text while SQL Server renders it uppercase, so an operator's copy-pasted id no longer depends on which store answered.

If you relied on a tag name or value being matched case-sensitively, this query now matches more rows than it used to. An unregistered tag name is still an ArgumentException listing what is registered, never an empty answer.

CompactStreamAsync<T> infers the fold for an unregistered aggregate (#​5366)

Compaction refused any type without a registered aggregation projection, while Fisher and Polecat both inferred one. That made a compaction policy able to target only an aggregate the application already snapshots — a real constraint on the feature, invisible until runtime, and one nobody would infer from the API.

CompactStreamAsync<T> now builds the aggregator from T's own Create/Apply conventions when no projection is registered, which is what every other aggregation path in Marten already did. The typed overload names T outright, and that is the same declaration of intent a registration would be.
... (truncated)

9.32.1

A bug-fix release, on JasperFx 2.66.0 and Weasel 9.31.0. The headline is that Native AOT document reads worked for nobody before this; the rest is correctness work on natural keys, the outbox, EF Core projections, and flat tables.

Several of these change observable behaviour. Each one is called out below.

Native AOT: reading a document from a native binary (#​5328)

An app published with PublishAot=true built and published analyzer-clean, then threw on the first document read. Writes — including schema creation — were fine. Three separate sites, all on the read path:

MissingMethodException: No parameterless constructor defined for type
'Marten.Internal.Sessions.QuerySession+StorageFinder`1[MyDocument]'
PlatformNotSupportedException: Dynamic code generation is not supported on this platform.
   at FastExpressionCompiler.ExpressionCompiler.CompileFast[R](...)
   at Marten.Linq.Parsing.LinqInternalExtensions.ReduceToConstant(Expression)

QuerySession.StorageFor(Type) and CompiledQueryPlan.sortMembers() both went from a runtime Type back to a closed generic through MakeGenericType + Activator.CreateInstance, and ILC has no native code for an instantiation nothing constructs statically. The third — expression compilation for every non-literal value in a where clause, i.e. every where x.Member == captured — is Reflection.Emit, which native AOT does not have at all.

All three now capture the closed generic where the compiler has already emitted it. LoadAsync, LINQ, raw SQL, Include, patching and source-generated compiled queries all run from a native binary.

Two shapes still need a JIT, and both have a one-line workaround (documented in the AOT publishing guide): a compiled query with an enum-typed parameter, and a where clause whose value comes from a method call — hoist the call into a local first.

Marten also ships a new runtime gate, src/Marten.AotRuntimeSmoke, which publishes natively and runs every read path against a real PostgreSQL in CI. The existing Marten.AotSmoke is a build-time analyzer gate and was structurally blind to this class of defect: the offending code analyzes clean and throws one line later. If you publish AOT, do the same in your own pipeline — a clean publish is necessary, not sufficient.

Natural keys: three fixes, two of them behavioural (#​5344)

The first run of the shared NaturalKeyCompliance suite against Marten found three genuine bugs in the [NaturalKey] lookup table.

⚠️ Behavior change. A second stream claiming a natural key a live stream already holds is now refused with JasperFx.Events.DuplicateNaturalKeyException, carrying both ExistingStreamId and ClaimingStreamId. Previously the claim silently succeeded and repointed the lookup row at the newcomer. The refusal is in the SQL rather than a pre-flight read, so concurrent claimants serialize on the row lock ON CONFLICT already takes and the existing mapping is never repointed even momentarily. Re-asserting your own mapping is still idempotent, and a key held by an archived stream is still claimable.

If two live streams in your database already share a key — which older Marten permitted, because the upsert repointed — the rebuild path deliberately stays last-writer-wins so a pre-existing data condition cannot turn into an unrecoverable daemon failure.

⚠️ Behavior change. An archived stream no longer resolves by its natural key. ArchiveStream left the lookup row intact, so an archived stream stayed resolvable indefinitely. The flag is now read off the mt_streams row the lookup query already joins, which covers the explicit call, the Archived marker event, and the daemon alike. No schema change and no migration of existing lookup tables.

A [NaturalKey] of primitive type string on a Guid-identity store now works. FindFetchPlan asserted string stream storage for any string TId and refused with "This Marten event store is configured to identify streams with Guids" before any planner saw it. Both that guard and its mirror image in NaturalKeyFetchPlanner now narrow to the store's own stream identity type. The ambiguous case is unchanged by design: on a string-identity store, FetchLatest<T, string>(value) still addresses the stream.

A failed unit of work no longer commits its outbox batch (#​5353)

IMessageBatch.BeforeCommitAsync was called before the UpdateBatch was built and before a transaction was even open — so it fired for units of work that had not been attempted, let alone succeeded. A SaveChangesAsync that then failed (a stream-id collision, say) left the message batch enlisted and committed, with no rollback hook able to un-enlist it.

The batch is now enlisted as an ITransactionParticipant, invoked inside the transaction after every page has executed and immediately before the COMMIT. A unit of work that fails never reaches it. Hook ordering and the inside/outside-the-transaction visibility contract are unchanged; the before hook simply runs later in the transaction. This is the placement Polecat already used.

EF Core projections stop generating a Marten document table (#​5351, #​5329)

Registering an EfCoreSingleStreamProjection / EfCoreMultiStreamProjection made Marten build a DocumentMapping for the aggregate type and emit schema for it, even though EF Core owns that data in a DbContext-mapped table Marten never reads or writes. Two consequences, both fixed:

  • ApplyAllConfiguredChangesToDatabaseAsync() created an empty mt_doc_<tdoc> table nothing used — and in the normal AutoCreate.None shape, where schema is built by a migration pass that does not register the projection, AssertDatabaseMatchesConfigurationAsync() then reported that table as MISSING and a correctly-migrated database failed its own startup assertion.
    ... (truncated)

9.32.0

Two coordinated feature waves against JasperFx.Events 2.63.0, plus the compliance enrollments that keep Marten, Polecat, and Fisher synchronized.

Broadened event querying (jasperfx#​737 → #​5330 / #​5331)

QueryEventsAsync / EventQuery now supports inclusive timestamp windows (TimestampFrom/TimestampTo), inclusive sequence windows (SequenceFloor/SequenceCeiling), multiple event type aliases (EventTypeNames, union with the single EventTypeName), and DCB tag conditions folded into the query (TagConditions — the TagTables and HStore modes both translate through the same SQL as QueryByTagsAsync). Results are contractually ordered sequence-ascending; TotalCount counts matches across all pages.

⚠️ Behavior change: a metadata filter (CorrelationId, CausationId, UserName) against a store that has not enabled the corresponding capture column now throws NotSupportedException naming the field — previously the filter was silently dropped and unfiltered results were returned as if filtered.

Stream state querying + the compaction watermark (jasperfx#​740 → #​5333 / #​5334)

QueryStreamStates(tenantId?) exposes a real IQueryable<StreamState> over mt_streams — every public member translates in Where(), including AggregateType == typeof(X) (resolved through the stored type alias) and the new CompactedVersion watermark, so a compaction policy predicate like s.AggregateType == typeof(Order) && s.Version - s.CompactedVersion > 1000 && !s.IsArchived runs server-side. mt_streams gains an additive compacted_version bigint NOT NULL DEFAULT 0 column (Weasel migration; expect a one-time schema-touch burst on a first local test run against pre-existing schemas), and CompactStreamAsync records the watermark in the same unit of work — monotonically (greatest()), so a replayed lower-cutoff compaction can never move it backwards. Untranslatable members and a tenant scope on a non-conjoined store refuse by name, never silently match-all.

CLI + compliance

The JasperFx.Events event-query and stream-query commands are covered end-to-end against Marten (first CLI-execution tests in the repo). Compliance: EventQueryCompliance (41 facts) and StreamStateQueryCompliance (15) enrolled; the full compliance namespace runs 388/388 on net9.0 and net10.0.

Note for code importing both Marten and JasperFx.Events.Documents: extension-style ToListAsync over the stream queryable is ambiguous (CS0121) — call the JasperFx extensions explicitly.

🤖 Generated with Claude Code

9.31.2

A diagnostics-only patch release. No behavioural change to the write path.

A failed batched write now reports the SQL that failed

When a batched write failed, Marten threw a MartenCommandException whose message rendered an empty command text. Reported from the field on an async projection:

Marten Command Failure:$ $ $ 42601: syntax error at end of input POSITION: 56
Error trying to build and apply changes to event subscription MyProjection:All

The $ $ $ is the message template interpolating a command that was always null. ReadNpgsqlCommand() looks for an NpgsqlCommand in exception.Data, and on the ExecuteBatchPagesAsync path there is no single command to put there — AutoClosingLifetime transformed the exception with nothing recorded at all, and WrapAndThrow(NpgsqlBatch, ...) recorded the batch under a key that nothing ever read.

The practical consequence: the offending SQL was unrecoverable from the exception and from the logs, for every async projection write. That is precisely the situation where hand-written SQL — a QueueSqlCommand, a custom IStorageOperation, a projection side effect — turns out to be malformed, and the one thing you need to see is the statement.

MartenCommandException now recovers the SQL from three sources, in order:

  1. the NpgsqlCommand it was handed;
  2. NpgsqlException.BatchCommand — Npgsql identifies the exact statement the server rejected when the failure came out of a batch execution;
  3. an NpgsqlBatch recorded on the exception.

The recovered statement is also exposed on a new MartenCommandException.CommandText property, so it can be read programmatically rather than scraped out of the message.

AutoClosingLifetime executes its batches inline rather than through handleCommandException, so it now records its batch. The other three connection lifetimes already routed through WrapAndThrow(NpgsqlBatch, ...) and only needed a reader for the key they were already writing.

Two guardrails worth knowing about: a batch renders at most five statements, so a 500-operation projection page cannot turn one failure into an unreadable log entry, and the whole resolution is wrapped in a catch — building a diagnostic must never replace the real failure.

Also released as 8.38.1 on the 8.x line.

9.31.1

A single-fix patch release that moves Marten to JasperFx 2.61.0, picking up the source generator fix for JasperFx/jasperfx#​733. Weasel stays on 9.29.0.

Self-aggregating snapshots silently skipped a constructor-based Create

A self-aggregating snapshot may declare its Create handler as an event-shaped constructor, public Foo(FooCreated e), instead of a named static Create. That works on its own. Adding a ShouldDelete method to the same aggregate switched the source generator to a different emitter — one that built its dispatch switch from named conventional methods only — so the constructor's event type got no case arm at all.

Nothing failed loudly. The generated code compiled, the constructor never ran, and the next Apply-only event built the aggregate through RuntimeHelpers.GetUninitializedObject, skipping every field initializer. The visible symptom was an ApplyEventException wrapping a NullReferenceException out of an Apply that appended to a collection property:

JasperFx.Events.Daemon.ApplyEventException: Failure to apply event #​0 Id(...)
 ---> System.NullReferenceException: Object reference not set to an instance of an object.
   at TempGuidAggregate.Apply(TagAdded @​event)

The quieter outcome, where no Apply happens to dereference anything, is a silently blank aggregate.

Both documented workarounds — converting the constructor to a static Create, or registering the delete through DeleteEvent<T>() instead of ShouldDelete — become unnecessary on this release.

The same omission was also in the generated EventTypes property on every self-aggregating path, including the ones whose dispatch was already correct, so an aggregate with a constructor Create and no ShouldDelete gains its creating event in that list here too.

Covered by Bug_jasperfx_733_event_constructor_create_with_should_delete across inline, async and live aggregation, plus the delete arm itself. See #​5322.

9.31.0

Every fix in this release removes a silent wrong answer — not a crash, not an exception, but a plausible-looking result that was wrong with nothing to tell you so. A stream folded into partial aggregates. Paging that returned arbitrary pages. Monitoring that went dark rather than broken. A search reading a key that did not exist. An index that was never created.

Plus one genuinely new feature: weighted full text search with relevance ranking.

New: weighted full text indexes and ts_rank ordering

A full text index used to concatenate its members into one flat vector, so a match in a title was exactly as relevant as a match in a long description. Now it can be weighted, and ranked:

opts.Schema.For<Achievement>().WeightedFullTextIndex(idx => idx
    .Weighted(a => a.Title, TextSearchWeight.A)
    .Weighted(a => a.Tagline, TextSearchWeight.B)
    .Weighted(a => a.Description, TextSearchWeight.C));

var results = await session.Query<Achievement>()
    .Where(a => a.WebStyleSearch(term))
    .OrderByTextRank(term, TextSearchFunction.WebStyle)
    .ToListAsync();

The rank resolves the same tsvector the Where clause matched on, read from the index definition. That is the load-bearing design constraint rather than an implementation detail: a rank computed over a different vector than the filter matched on returns rows in an order that looks plausible and means nothing — a far quieter failure than returning the wrong rows.

Requires Weasel 9.29.0 (weasel#​541), which made it possible to index an expression that is already a tsvector. See the full text documentation for the costs worth knowing first — GIN cannot order, so this is a post-filter sort; and adding weights to an existing index drops and recreates it.

Silent wrong answers, fixed

Async projections folded one stream into several partial aggregates (#​5305, originally #​4085). When a stream's events disagreed about tenant_id, the daemon sliced it per tenant and applied the pieces over each other, so an Apply saw a document with every property at its default. Marten had set ForceSingleTenancy since the original fix, but TenantedEventSlicer honoured that flag on only one of its two overloads — and the async daemon reaches the other one. The flag was being set on precisely the path that could not read it. Fixed upstream in JasperFx 2.58.0.

OrderBy after a GroupJoin/SelectMany was dropped from the SQL (#​5311). Silently, and with Skip/Take it was worse: OFFSET and LIMIT were emitted while the ordering was not, and unordered paging in PostgreSQL has no stable row order — so rows repeat across pages while others never appear. Where on a bare-side selector and Count over a join were wrong in the same surface and are fixed too.

A store with monitoring off dropped the monitoring columns another store had added (#​5309). Two DocumentStores over one DatabaseSchemaName that disagreed about EnableExtendedProgressionTracking kept stripping each other's mt_event_progression columns — last writer wins, silent on both sides. A service beside a seeder or a reporting job is an ordinary arrangement, and neither was doing anything wrong. The shape of that table no longer depends on configuration at all.

Full text search rewrote JSON keys, not just the column (#​5314, thanks @​mlh758). A member whose serialized name contained data produced d.data ->> 'd.data' — a key that does not exist. Two quiet consequences: that member contributed nothing to the search, and the expression no longer matched the index, so the GIN index could not serve the query either.

A second full text index over different members was silently discarded (#​5315). Index names derive from the table rather than from the members, so two FullTextIndex() calls collided by construction and the second was thrown away — never registered, never created, never searched. Ambiguity is now refused with an explanatory exception instead of resolved by declaration order.

A DCB boundary aggregate failed far from its cause (JasperFx 2.60.0). An identity-less [BoundaryAggregate] folded by Evolve(IEvent) got no evolver and no diagnostic, surfacing much later as FetchForWritingByTags<T> throwing "No source-generated dispatcher found" — naming neither the type nor the reason. Now it generates, and a [BoundaryAggregate] with nothing to fold events with is reported as JFXEVT007.

A DCB tag version was captured after the events were read (#​5300). Two batched statements do not share a READ COMMITTED snapshot, so the version could reflect a concurrent append. Captured before the read now.

Also in this release

  • A projection-run CLI command arrives with JasperFx 2.60.0 and needs no Marten change — every host referencing JasperFx.Events picks it up. It replays one projection over a stream slice or DCB tag match and prints per-event before/after state, writing nothing.
  • Bug_5268's concurrent index test no longer depends on how much unrelated transaction load happens to be in flight (#​5308), so validating a dependency bump by running the whole suite at once is trustworthy again.
  • BuildSlicer now agrees with FetchAsyncPlan about global aggregates (#​5307). Alignment rather than a fix — no reachable corruption depended on it, and the reasoning is written down at the call site so the question does not have to be reconstructed next time.

Upgrade notes

OrderByFragment.Expressions changed from List<string> to List<ISqlFragment>. A public API break, deliberately taken: an ordering could not carry a parameter while the clause was a list of strings, which is why the older ngram ranking inlines its search term rather than binding it. Nothing outside Marten's own LINQ internals is likely to touch this type, but it is a compile break if you did.

... (truncated)

Commits viewable in compare view.

Pinned Quartz.Extensions.Hosting at 3.21.0.

Release notes

Sourced from Quartz.Extensions.Hosting's releases.

3.21.0

Quartz.NET 3.21.0 carries the three fixes held back from 3.20.1 because each needed a small addition to the public surface or changed what a running scheduler does. All three were found while 4.0 was being finished; each is as old as 3.x. The public API grows by two interfaces on one class and nothing else; the schema is 3.20's. Three of the changes alter behaviour, each marked Behavior change worth noting below.

dotnet add package Quartz --version 3.21.0

What changed

  • ResumeAll clears every paused trigger group, not only the ones with triggers — the persistent store resumed the groups it found in QRTZ_TRIGGERS and then deleted only its all-groups marker, so a group paused while it held no triggers kept its QRTZ_PAUSED_TRIGGER_GRPS row and went on pausing whatever was scheduled into it afterwards. Pausing a group before anything is scheduled into it is a documented use of the exact-name matcher, so this was a row the store wrote on purpose and could not take back. The trailing delete now takes every group, as RAMJobStore has always done. (#​3721, #​3742, port of f76b04a468)
    • Behavior change worth noting: a group paused while empty no longer survives ResumeAll.
  • A job store closes its lock handler when it shuts down — RedisSemaphore opened a ConnectionMultiplexer on the first lock and kept it, with its heartbeat, for the life of the process, because nothing on the store's shutdown path reached the lock handler and ISemaphore had no member that meant "we are done". On a branch that targets netstandard2.0 and net462 an interface cannot gain a default member, so JobStoreSupport.Shutdown now disposes a lock handler that implements IAsyncDisposable or IDisposable, after the misfire handler, the cluster manager and the connection manager have stopped, logging and continuing if that throws; RedisSemaphore implements both and closes the multiplexer it opened. (#​3721, #​3742, port of #​3639)
    • Behavior change worth noting: a custom ISemaphore that implements either interface is now disposed at shutdown.
  • A configured wait longer than the platform's timer ceiling is refused where it is set — Task.Delay refuses anything longer than about 49.7 days on .NET and about 24.9 days on .NET Framework, with an ArgumentOutOfRangeException naming a parameter called delay, and every duration Quartz waits out that way was accepted unchecked and reported later from wherever the wait happened. MisfireHandlerFrequency, MisfireThreshold (when it is also the handler's sleep), ClusterCheckinInterval, DbRetryInterval, TransientRetryInterval, the row-lock handlers' RetryPeriod, StartDelayed's argument and QuartzHostedServiceOptions.StartDelay now name the setting, the ceiling and the value at configuration time. The ceiling is per target framework, held to what Task.Delay actually accepts by a test. (#​3721, #​3742, port of #​3577)
    • Behavior change worth noting: a value past the ceiling now fails at configuration rather than later.

Public API — additive only

  • Quartz.Extensions.Redis: RedisSemaphore implements IAsyncDisposable and IDisposable.

Upgrading

dotnet add package Quartz --version 3.21.0. Nothing to migrate. The 4.0 line is the current major; the 4.x migration guide is the way there, and 4.0.1 made the upgrade one a dependency bot can offer.

Full changelog: quartznet/quartznet@v3.20.1...v3.21.0

3.20.1

Quartz.NET 3.20.1 is a maintenance release: every change is a bug fix, the public API is untouched (the baselines did not move), and the schema is 3.20's. Most of it was found while 4.0 was being finished and rehearsed — a fix that turned out to be as old as 3.x was ported here rather than left on the newer line — and one item comes from a production application's 3.19.1 → 4.0 upgrade that also read on 3.x. Eight of the fixes change what a running scheduler does, each marked Behavior change worth noting below.

dotnet add package Quartz --version 3.20.1

What changed

Landed on the branch since 3.20.0:

  • A DailyTimeIntervalTrigger stored through the default Newtonsoft path reads back again — TimeOfDay has no parameterless constructor, so with the trigger converters off (the default) EndTimeOfDay threw "Unable to find a constructor" and StartTimeOfDay silently read back as midnight. A converter scoped to TimeOfDay-typed members reads both forms; nothing about what is written changed, so every blob a released 3.20 wrote is one this reads. (9ee33fec17, fixes #​3508)
  • A daily time interval trigger never fires before it starts — StartTimeUtc kept its milliseconds while the fire times are counted in whole seconds, so a start of 22:50:00.68 could produce a first fire at 22:50:00.000. Start and end are rounded down to the second when set, as CronTriggerImpl always did. (cc051a7788, #​3386)
  • A trigger with nothing left to fire is finished however its last firing ended — a firing abandoned by a failing job listener, a veto or a shutdown left a one-shot trigger waiting for ever in RAMJobStore and as a permanent COMPLETE row in the ADO store. Both stores finish it now. (0af9431d3e, #​3507)
  • The in-memory store applies the misfire policy of a trigger it unblocks — a trigger blocked behind a [DisallowConcurrentExecution] job is neither acquired nor swept, so the completion that unblocks it is the first thing that can settle its missed fire time; RAMJobStore now does what JobStoreSupport.RecoverUnblockedMisfires always did. (c9d8658a35, #​3463)
  • Pausing a trigger no longer throws its error away — RAMJobStore wrote Paused over Error, so a failed trigger vanished from every listing once its group was paused and ResetTriggerFromErrorState had nothing to reset. It now pauses only what the ADO store pauses: waiting, acquired and blocked triggers. (a56a16ca0c)
    • Behavior change worth noting: 3.20.0's notes listed this among the store-parity alignments left off 3.x; it is on 3.x now.
  • Rescheduling recomputes a fire time its new start time left behind — RescheduleJob advanced a never-fired repeating simple trigger's start time past a next fire time it kept, so it fired at the stale time and again at its start. (3e086091fc, #​3554)
  • A trigger loaded beside its job by the XML processor is scheduled once, not twice — every such trigger was scheduled and then immediately rescheduled with overwrite-existing-data on, and a repeating trigger that starts now fired twice milliseconds apart. (f25080cef6, #​3554)
  • Both serializers read a string dictionary written by the other — a Dictionary<string, string> job-data value written by the Newtonsoft package carried a $type the System.Text.Json reader handed back as an entry, and one written by System.Text.Json came back from Json.NET as a JObject. Both readers read both shapes; neither writer changed. (83ba80ce79, part of #​3582)
  • Schema validation checks QRTZ_SIMPROP_TRIGGERS too — a database missing only that table passed validation and failed on the first calendar-interval, daily-time-interval or recurrence trigger insert. (b33c70487b, #​3564)
  • The 3.20 index-alignment scripts name the 4.0 index script that supersedes them (4b3c43a90e); untagged builds from the branch say 3.20 (0e2f6bcf31); the XML scheduling integration test opens its own fixture's data source (4c07210199, #​3573).

Ported from 4.0:

  • A job listener that throws before it returns no longer wedges the firing — a synchronous throw from JobToBeExecuted escaped as itself rather than the exception the run shell catches, so TriggeredJobComplete was never reached: the trigger stayed acquired and, for a [DisallowConcurrentExecution] job, every sibling trigger stayed blocked; the firing was also listed as executing for the life of the process. (port of #​3502)
  • MySQL's misfire sweep reads the index that has the shape it needs — both misfire statements and the count every misfire pass starts with were forced onto IDX_QRTZ_T_NFT_ST_MISFIRE, whose second column is compared with <> and stops the seek dead. Measured on 4.x against 100,000 triggers: the count 111 ms → 0.7 ms, the sweep 66 ms → 0.7 ms. No schema change. (port of #​3608)
  • A process that cannot load the job classes can edit their schedules — RescheduleJob and UpdateTriggerDetails on the ADO store resolved the job's class to decide whether the new trigger could run, and failed in an administration node without the assembly. Both read the job's two attribute flags from QRTZ_JOB_DETAILS now, so the decision is right without the class and a placeholder ITypeLoadHelper — which decided that question by whether the placeholder carried the attribute — is no longer needed. (port of #​3705)
  • A transaction the database rolled back is retried whatever the driver calls it — SQLSTATE class 40 (40001, 40P01; 40002 excepted) is transient. Firebird reports a write conflict that way with IsTransient false, and MySql.Data its 1213 deadlock. (port of #​3454)
    • Behavior change worth noting: those failures are retried where they were treated as permanent.
  • A persistent store refuses a repeat interval it cannot hold — a SimpleTrigger interval finer than a millisecond was stored as 0, read back as zero, and left the trigger in ACQUIRED for good behind a divide-by-zero the store logged and swallowed. It is refused on write now, naming the trigger and the column; RAMJobStore keeps accepting it. (port of #​3673)
    • Behavior change worth noting: storing such a trigger throws where it used to succeed and leave the trigger stuck; a trigger already stored with a zero interval is unaffected by this release.
  • System.Text.Json refuses a job-data value it cannot read back — a List<string> or a nested object serialized happily and threw on the next read with the blob already in the database, and every later acquisition of the trigger failed on it. A value that would be stored as a JSON array, or as an object other than a Dictionary<string, string>, is refused before the first byte is written, naming the entry and its type. Anything stored as a number or a string — every numeric type, DateTime, Guid, byte[], Uri — still round-trips exactly as before. (port of #​3495)
    • Behavior change worth noting: such a value is a JsonSerializationException at store time, where it used to be a blob the next read failed on. The refusal also covers three shapes that did not throw before but never came back as themselves either — a non-generic Hashtable, an object whose properties are all strings, and a JobDataMap nested inside a JobDataMap — each of which the reader handed back as a Dictionary<string, string>. Store one of those as a string of your own making.
  • A daily time interval trigger stops at its end time — an EndTimeUtc falling between two fire times of the same day let the trigger go on firing until the daily window closed, and FinalFireTimeUtc reported that close even when it was a day past the end. (port of the daily half of #​3458)
    • Behavior change worth noting: a trigger whose end time fell mid-window fires fewer times than on 3.20.
  • NativeJob no longer deadlocks a child that writes more than a pipe buffer — both streams were redirected whether or not consumeStreams asked for them to be read, so with the defaults a chatty process blocked on its own write and the job's synchronous wait held a worker for ever. Nothing is redirected unless consumed. (port of the rc.1 fix)
  • A connection that cannot join the ambient transaction is refused — EnlistConnection inside a TransactionScope took a Microsoft.Data.Sqlite connection on trust, and SQLite cannot enlist, so every statement committed on the spot and a rolled-back scope left the schedule behind. EnlistTransaction(DbTransaction) still works there. (port of the beta.1 fix)
  • The misfire threshold instant itself is late, on every store — a trigger due at exactly now - MisfireThreshold was a misfire to RAMJobStore and to the ADO store's single-trigger path but not to its periodic sweep; the sweep says <= now and the acquisition predicate moved to > in step. (port of #​3462)
    • Behavior change worth noting: a trigger due at exactly that instant is swept as a misfire where the periodic sweep used to pass over it, so its misfire instruction now applies to it.
  • A job-data number written with a decimal comma is unreadable, not a hundredfold of itself — "3,14" read as 314 from the floating-point accessors while GetInt threw. (port of the beta.1 fix)
    • Behavior change worth noting: GetDouble and GetFloat throw a FormatException for such a string where they used to answer a number a hundred times too large.
  • A group matcher containing [ matches literally on SQL Server — T-SQL reads [ as a character class in LIKE; it is escaped on that dialect only, because the standard forbids escaping a non-wildcard elsewhere. (port of the rc.1 fix)
    • Behavior change worth noting: a matcher whose value contains [ matches the groups it names rather than the character class T-SQL read it as, so it can list, pause, resume or delete a different set than on 3.20.
  • DirectoryScanJob stores its previous scan as something a job store can write — it kept a List<FileInfo> under [PersistJobDataAfterExecution], which System.Text.Json cannot write, so its first firing on such a store failed to persist. A legacy list already in a running scheduler is still read. (port of the rc.1 fix)
  • DirectoryScanJob runs at all on 3.20 — it read its optional job-data keys with GetString, which throws for a key that is not there, so a job configured without a directory provider or listener name failed on every firing with KeyNotFoundException. Found while porting the previous item; the optional keys are asked for rather than read.
  • SelectSchedulerStateRecords binds its parameters in statement order — only a provider with BindByName off could ever have noticed. (port of the alpha.3 fix)

Public API

Unchanged. No signature was added, altered or removed, and the PublicApiTest baselines did not move.
... (truncated)

Commits viewable in compare view.

Pinned Quartz.Serialization.SystemTextJson at 3.21.0.

Release notes

Sourced from Quartz.Serialization.SystemTextJson's releases.

3.21.0

Quartz.NET 3.21.0 carries the three fixes held back from 3.20.1 because each needed a small addition to the public surface or changed what a running scheduler does. All three were found while 4.0 was being finished; each is as old as 3.x. The public API grows by two interfaces on one class and nothing else; the schema is 3.20's. Three of the changes alter behaviour, each marked Behavior change worth noting below.

dotnet add package Quartz --version 3.21.0

What changed

  • ResumeAll clears every paused trigger group, not only the ones with triggers — the persistent store resumed the groups it found in QRTZ_TRIGGERS and then deleted only its all-groups marker, so a group paused while it held no triggers kept its QRTZ_PAUSED_TRIGGER_GRPS row and went on pausing whatever was scheduled into it afterwards. Pausing a group before anything is scheduled into it is a documented use of the exact-name matcher, so this was a row the store wrote on purpose and could not take back. The trailing delete now takes every group, as RAMJobStore has always done. (#​3721, #​3742, port of f76b04a468)
    • Behavior change worth noting: a group paused while empty no longer survives ResumeAll.
  • A job store closes its lock handler when it shuts down — RedisSemaphore opened a ConnectionMultiplexer on the first lock and kept it, with its heartbeat, for the life of the process, because nothing on the store's shutdown path reached the lock handler and ISemaphore had no member that meant "we are done". On a branch that targets netstandard2.0 and net462 an interface cannot gain a default member, so JobStoreSupport.Shutdown now disposes a lock handler that implements IAsyncDisposable or IDisposable, after the misfire handler, the cluster manager and the connection manager have stopped, logging and continuing if that throws; RedisSemaphore implements both and closes the multiplexer it opened. (#​3721, #​3742, port of #​3639)
    • Behavior change worth noting: a custom ISemaphore that implements either interface is now disposed at shutdown.
  • A configured wait longer than the platform's timer ceiling is refused where it is set — Task.Delay refuses anything longer than about 49.7 days on .NET and about 24.9 days on .NET Framework, with an ArgumentOutOfRangeException naming a parameter called delay, and every duration Quartz waits out that way was accepted unchecked and reported later from wherever the wait happened. MisfireHandlerFrequency, MisfireThreshold (when it is also the handler's sleep), ClusterCheckinInterval, DbRetryInterval, TransientRetryInterval, the row-lock handlers' RetryPeriod, StartDelayed's argument and QuartzHostedServiceOptions.StartDelay now name the setting, the ceiling and the value at configuration time. The ceiling is per target framework, held to what Task.Delay actually accepts by a test. (#​3721, #​3742, port of #​3577)
    • Behavior change worth noting: a value past the ceiling now fails at configuration rather than later.

Public API — additive only

  • Quartz.Extensions.Redis: RedisSemaphore implements IAsyncDisposable and IDisposable.

Upgrading

dotnet add package Quartz --version 3.21.0. Nothing to migrate. The 4.0 line is the current major; the 4.x migration guide is the way there, and 4.0.1 made the upgrade one a dependency bot can offer.

Full changelog: quartznet/quartznet@v3.20.1...v3.21.0

3.20.1

Quartz.NET 3.20.1 is a maintenance release: every change is a bug fix, the public API is untouched (the baselines did not move), and the schema is 3.20's. Most of it was found while 4.0 was being finished and rehearsed — a fix that turned out to be as old as 3.x was ported here rather than left on the newer line — and one item comes from a production application's 3.19.1 → 4.0 upgrade that also read on 3.x. Eight of the fixes change what a running scheduler does, each marked Behavior change worth noting below.

dotnet add package Quartz --version 3.20.1

What changed

Landed on the branch since 3.20.0:

  • A DailyTimeIntervalTrigger stored through the default Newtonsoft path reads back again — TimeOfDay has no parameterless constructor, so with the trigger converters off (the default) EndTimeOfDay threw "Unable to find a constructor" and StartTimeOfDay silently read back as midnight. A converter scoped to TimeOfDay-typed members reads both forms; nothing about what is written changed, so every blob a released 3.20 wrote is one this reads. (9ee33fec17, fixes #​3508)
  • A daily time interval trigger never fires before it starts — StartTimeUtc kept its milliseconds while the fire times are counted in whole seconds, so a start of 22:50:00.68 could produce a first fire at 22:50:00.000. Start and end are rounded down to the second when set, as CronTriggerImpl always did. (cc051a7788, #​3386)
  • A trigger with nothing left to fire is finished however its last firing ended — a firing abandoned by a failing job listener, a veto or a shutdown left a one-shot trigger waiting for ever in RAMJobStore and as a permanent COMPLETE row in the ADO store. Both stores finish it now. (0af9431d3e, #​3507)
  • The in-memory store applies the misfire policy of a trigger it unblocks — a trigger blocked behind a [DisallowConcurrentExecution] job is neither acquired nor swept, so the completion that unblocks it is the first thing that can settle its missed fire time; RAMJobStore now does what JobStoreSupport.RecoverUnblockedMisfires always did. (c9d8658a35, #​3463)
  • Pausing a trigger no longer throws its error away — RAMJobStore wrote Paused over Error, so a failed trigger vanished from every listing once its group was paused and ResetTriggerFromErrorState had nothing to reset. It now pauses only what the ADO store pauses: waiting, acquired and blocked triggers. (a56a16ca0c)
    • Behavior change worth noting: 3.20.0's notes listed this among the store-parity alignments left off 3.x; it is on 3.x now.
  • Rescheduling recomputes a fire time its new start time left behind — RescheduleJob advanced a never-fired repeating simple trigger's start time past a next fire time it kept, so it fired at the stale time and again at its start. (3e086091fc, #​3554)
  • A trigger loaded beside its job by the XML processor is scheduled once, not twice — every such trigger was scheduled and then immediately rescheduled with overwrite-existing-data on, and a repeating trigger that starts now fired twice milliseconds apart. (f25080cef6, #​3554)
  • Both serializers read a string dictionary written by the other — a Dictionary<string, string> job-data value written by the Newtonsoft package carried a $type the System.Text.Json reader handed back as an entry, and one written by System.Text.Json came back from Json.NET as a JObject. Both readers read both shapes; neither writer changed. (83ba80ce79, part of #​3582)
  • Schema validation checks QRTZ_SIMPROP_TRIGGERS too — a database missing only that table passed validation and failed on the first calendar-interval, daily-time-interval or recurrence trigger insert. (b33c70487b, #​3564)
  • The 3.20 index-alignment scripts name the 4.0 index script that supersedes them (4b3c43a90e); untagged builds from the branch say 3.20 (0e2f6bcf31); the XML scheduling integration test opens its own fixture's data source (4c07210199, #​3573).

Ported from 4.0:

  • A job listener that throws before it returns no longer wedges the firing — a synchronous throw from JobToBeExecuted escaped as itself rather than the exception the run shell catches, so TriggeredJobComplete was never reached: the trigger stayed acquired and, for a [DisallowConcurrentExecution] job, every sibling trigger stayed blocked; the firing was also listed as executing for the life of the process. (port of #​3502)
  • MySQL's misfire sweep reads the index that has the shape it needs — both misfire statements and the count every misfire pass starts with were forced onto IDX_QRTZ_T_NFT_ST_MISFIRE, whose second column is compared with <> and stops the seek dead. Measured on 4.x against 100,000 triggers: the count 111 ms → 0.7 ms, the sweep 66 ms → 0.7 ms. No schema change. (port of #​3608)
  • A process that cannot load the job classes can edit their schedules — RescheduleJob and UpdateTriggerDetails on the ADO store resolved the job's class to decide whether the new trigger could run, and failed in an administration node without the assembly. Both read the job's two attribute flags from QRTZ_JOB_DETAILS now, so the decision is right without the class and a placeholder ITypeLoadHelper — which decided that question by whether the placeholder carried the attribute — is no longer needed. (port of #​3705)
  • A transaction the database rolled back is retried whatever the driver calls it — SQLSTATE class 40 (40001, 40P01; 40002 excepted) is transient. Firebird reports a write conflict that way with IsTransient false, and MySql.Data its 1213 deadlock. (port of #​3454)
    • Behavior change worth noting: those failures are retried where they were treated as permanent.
  • A persistent store refuses a repeat interval it cannot hold — a SimpleTrigger interval finer than a millisecond was stored as 0, read back as zero, and left the trigger in ACQUIRED for good behind a divide-by-zero the store logged and swallowed. It is refused on write now, naming the trigger and the column; RAMJobStore keeps accepting it. (port of #​3673)
    • Behavior change worth noting: storing such a trigger throws where it used to succeed and leave the trigger stuck; a trigger already stored with a zero interval is unaffected by this release.
  • System.Text.Json refuses a job-data value it cannot read back — a List<string> or a nested object serialized happily and threw on the next read with the blob already in the database, and every later acquisition of the trigger failed on it. A value that would be stored as a JSON array, or as an object other than a Dictionary<string, string>, is refused before the first byte is written, naming the entry and its type. Anything stored as a number or a string — every numeric type, DateTime, Guid, byte[], Uri — still round-trips exactly as before. (port of #​3495)
    • Behavior change worth noting: such a value is a JsonSerializationException at store time, where it used to be a blob the next read failed on. The refusal also covers three shapes that did not throw before but never came back as themselves either — a non-generic Hashtable, an object whose properties are all strings, and a JobDataMap nested inside a JobDataMap — each of which the reader handed back as a Dictionary<string, string>. Store one of those as a string of your own making.
  • A daily time interval trigger stops at its end time — an EndTimeUtc falling between two fire times of the same day let the trigger go on firing until the daily window closed, and FinalFireTimeUtc reported that close even when it was a day past the end. (port of the daily half of #​3458)
    • Behavior change worth noting: a trigger whose end time fell mid-window fires fewer times than on 3.20.
  • NativeJob no longer deadlocks a child that writes more than a pipe buffer — both streams were redirected whether or not consumeStreams asked for them to be read, so with the defaults a chatty process blocked on its own write and the job's synchronous wait held a worker for ever. Nothing is redirected unless consumed. (port of the rc.1 fix)
  • A connection that cannot join the ambient transaction is refused — EnlistConnection inside a TransactionScope took a Microsoft.Data.Sqlite connection on trust, and SQLite cannot enlist, so every statement committed on the spot and a rolled-back scope left the schedule behind. EnlistTransaction(DbTransaction) still works there. (port of the beta.1 fix)
  • The misfire threshold instant itself is late, on every store — a trigger due at exactly now - MisfireThreshold was a misfire to RAMJobStore and to the ADO store's single-trigger path but not to its periodic sweep; the sweep says <= now and the acquisition predicate moved to > in step. (port of #​3462)
    • Behavior change worth noting: a trigger due at exactly that instant is swept as a misfire where the periodic sweep used to pass over it, so its misfire instruction now applies to it.
  • A job-data number written with a decimal comma is unreadable, not a hundredfold of itself — "3,14" read as 314 from the floating-point accessors while GetInt threw. (port of the beta.1 fix)
    • Behavior change worth noting: GetDouble and GetFloat throw a FormatException for such a string where they used to answer a number a hundred times too large.
  • A group matcher containing [ matches literally on SQL Server — T-SQL reads [ as a character class in LIKE; it is escaped on that dialect only, because the standard forbids escaping a non-wildcard elsewhere. (port of the rc.1 fix)
    • Behavior change worth noting: a matcher whose value contains [ matches the groups it names rather than the character class T-SQL read it as, so it can list, pause, resume or delete a different set than on 3.20.
  • DirectoryScanJob stores its previous scan as something a job store can write — it kept a List<FileInfo> under [PersistJobDataAfterExecution], which System.Text.Json cannot write, so its first firing on such a store failed to persist. A legacy list already in a running scheduler is still read. (port of the rc.1 fix)
  • DirectoryScanJob runs at all on 3.20 — it read its optional job-data keys with GetString, which throws for a key that is not there, so a job configured without a directory provider or listener name failed on every firing with KeyNotFoundException. Found while porting the previous item; the optional keys are asked for rather than read.
  • SelectSchedulerStateRecords binds its parameters in statement order — only a provider with BindByName off could ever have noticed. (port of the alpha.3 fix)

Public API

Unchanged. No signature was added, altered or removed, and the PublicApiTest baselines did not move.
... (truncated)

Commits viewable in compare view.

Pinned Radzen.Blazor at 11.3.2.

Release notes

Sourced from Radzen.Blazor's releases.

11.3.2

11.3.2 - 2026-09-04

Improvements

  • DataGrid auto-scrolls horizontally while a column header is dragged near either edge of the scroll container, so a column can be dropped onto a destination outside the visible area in one drag, in LTR and RTL (#​2690).
  • Demos link every component page to a markdown twin of its API reference.

Fixes

  • DataGrid, DataList, PivotDataGrid, DropDown, ListBox and DropDownDataGrid execute bound IQueryable data synchronously again, as in 11.2.8 and earlier. The asynchronous execution introduced in 11.3.0 released the renderer while a query still held the DbContext, so any other component on the page that enumerated a query from the same context during render, such as a DropDown in a FilterTemplate, threw "There is already an open DataReader associated with this Connection" or "A second operation was started on this context instance". Applications that need asynchronous execution use LoadData.
  • DataGrid reports zero items instead of one for an empty virtualized data source (#​2691).
  • DataGrid no longer leaks document mouse listeners when a column drag starts again before the previous one ended, and a quick click on Blazor Server no longer leaves a ghost header following the pointer until the next mouseup.
  • Tooltip measures the popup with the resolved side's arrow class before placing it, so a Bottom tooltip that falls back to Top no longer ends inside its target and flickers near the bottom of the viewport. https://forum.radzen.com/t/21690

11.3.1

11.3.1 - 2026-09-03

Improvements

  • DataGrid draws the focused cell during keyboard navigation and shows keyboard focus on grids without row selection (#​2698).
  • DataGrid filters array-valued properties on their element type, supports collection-level IsNull/IsEmpty filters without FilterProperty and null-guards Any/All item filters (#​2696).
  • Demos add a UI Blocks Gallery with 24 new block categories (#​2695).

Fixes

  • DataGrid, DropDown, ListBox and DropDownDataGrid no longer invoke application callbacks such as RowExpand, LoadChildData, RowSelect, RowEdit, Sort, Filter, Page and Change while their own asynchronous IQueryable load still holds the query provider. Pages sharing a DbContext threw "A second operation was started on this context instance" since 11.3.0. https://forum.radzen.com/t/21689
  • DataGrid tints frozen cells together with the rest of the focused row on grids without row selection.
  • Accordion and AutoComplete no longer throw "The ParameterView instance can no longer be read because it has expired" when Multiple or SelectedItem change at runtime. https://forum.radzen.com/t/21688

11.3.0

11.3.0 - 2026-09-02

Improvements

  • RadzenDataGrid, RadzenDataList, RadzenDropDown, RadzenListBox, RadzenDropDownDataGrid, RadzenPivotDataGrid - bound IQueryable count and materialization execute asynchronously when the provider supports it (e.g. Entity Framework Core) with no extra package or registration. Operations are serialized per query provider so components sharing a DbContext never issue concurrent operations on it, untranslatable queries fall back to the synchronous path and the Radzen.Blazor.DisableAsyncQueryExecution AppContext switch opts out entirely. Fixes #​2688.
  • RadzenDataGrid - render optimizations: reduced per-cell rendering overhead and row edit state is cascaded only while editing. Thanks to @​pianomanjh!
  • RadzenCheckBoxList, RadzenRadioButtonList, RadzenTree - selection membership is resolved once per render instead of scanning the selection per item, and compiled property getters are cached by signature - significantly faster rendering with large item and selection counts. Thanks to @​pianomanjh!
  • RadzenDatePicker - O(1) selected-day membership in Multiple-mode calendar. Thanks to @​pianomanjh!
  • RadzenPanelMenu - automatic indentation for nested items at any depth. Fixes #​2675.
  • QueryableExtension - filter MethodInfo lookups are cached and OrderBy expressions are parsed in a single pass. Thanks to @​pianomanjh!
  • Premium themes updated.

Fixes

  • RadzenRangeNavigator - no longer throws ArithmeticException when rendered before its width has been measured; NiceNumber now treats a non-finite range like a zero one. Fixes #​2693. Thanks to @​pianomanjh!
  • RadzenDataGrid - an empty grid with virtualization enabled no longer reports 1 record in the paging summary. Fixes #​2691.
  • RadzenPivotDataGrid - initially bound filter values are honored after interactive changes and no longer trigger a redundant Reload on initial render. Thanks to @​pianomanjh!
  • RadzenDropDown, RadzenListBox - filtering a virtualized list down to no matches no longer crashes the Blazor Server circuit.

Commits viewable in compare view.

Pinned WolverineFx.Marten at 6.36.0.

Release notes

Sourced from WolverineFx.Marten's releases.

6.36.0

Heads up when upgrading

  • CircuitBreaker() on a buffered local queue now stops the host from starting with an InvalidListenerConfigurationException (#​4410, #​4412). A buffered local queue can't pause, so the circuit breaker used to be accepted and then silently ignored. Add UseDurableInbox() to the queue, or opts.Policies.UseDurableLocalQueues(), or remove the circuit breaker. Durable local queues and external listeners are unaffected. If you use WolverineFx.AI with DurableQueue = false plus a circuit breaker on the callout queue, this applies to you too.
  • In the Event Model, a message handler forwarded from a gRPC RPC now always derives SlicePattern.Command (#​4413).

New

  • [SlicePattern] declares the Event Model slice pattern of a message handler whose message has no producer in the model, such as a message from another service or a hosted service (#​4395, #​4413). It only fills the gap: an HTTP route, gRPC RPC, schedule or inbound external system still decides the pattern.
  • A Wolverine gRPC client can be injected into a handler (#​4403, #​4409).
  • Each retry attempt's trace span links to the failed attempt before it with an ActivityLink (#​4398, #​4405).

Fixes

  • Agent assignment: each node now checks its assigned agents against the ones actually running and fixes any mismatch (#​3987, #​4404). The follow-up makes assignment deletes owner-scoped, adds claim-if-absent, and hardens the sweep (#​4407, #​4408).
  • Batching: the per-pipeline pending count now counts every batched message, not just listener arrivals (#​4397, #​4406).
  • Security: System.Security.Cryptography.Xml is now 10.0.12, clearing a high-severity advisory (#​4401).

Docs

  • Local queues and buffered endpoints use System.Threading.Channels, not TPL Dataflow (#​4411).
  • Removed the nonexistent EnableNodeAgentSupport() from the exclusive node processing page (#​4414).

6.35.0

Store operation side effects, everywhere

MartenOps covered store / insert / update / delete plus StartStream; anything else meant taking an
IDocumentSession and giving up on the handler being a pure function. All three stores now cover
what their own session API supports.

Op Marten Polecat Fisher
HardDelete, HardDeleteWhere, UndoDeleteWhere ✅ ✅ ✅
UpdateExpectedVersion ✅ ✅ —
UpdateRevision ✅ ✅ ✅
TryUpdateRevision ✅ — ✅
Patch, PatchWhere ✅ — ✅
QueueSqlCommand ✅ ✅ ✅
InsertObjects, DeleteObjects ✅ — —
Append, ArchiveStream ✅ ✅ ✅
UnArchiveStream, TombstoneStream — ✅ —

The gaps are deliberate: each set was checked against that store's own session API rather than copied
across, and an op whose Execute could only throw is worse than the absence of one. Polecat's last
row is the reverse case — two operations Marten has no counterpart for.

Every op also implements ITenantedMartenOp / ITenantedPolecatOp / ITenantedFisherOp, so one
extension scopes any of them while preserving the concrete return type:

MartenOps.ArchiveStream(command.OrderId).ForTenant(command.TenantId);
PolecatOps.StoreMany(items).ForTenant(tenantId).With(oneMore);

Thanks to @​erdtsieck for the Marten half, which is where this started.

Event Modeling: a declared model and the code now meet

Three findings from one comparison of a curated Event Model against the application built from it
(#​4385, #​4386, #​4387):

  • Slices join on handler type. Slice names are the merge key, and a board names a slice for the
    behaviour (ConfirmAppointment) while a derived source names it for the message type or the route.
    An eleven-slice application assembled as twenty-two with no disagreements — not because the sources
    agreed, but because they never met.
  • [Emits(typeof(...))] lets a handler nam....

Description has been truncated

Bumps Marten from 9.30.0 to 9.33.0
Bumps Quartz.Extensions.Hosting from 3.20.0 to 3.21.0
Bumps Quartz.Serialization.SystemTextJson from 3.20.0 to 3.21.0
Bumps Radzen.Blazor from 11.2.8 to 11.3.2
Bumps WolverineFx.Marten from 6.32.0 to 6.36.0
Bumps WolverineFx.RuntimeCompilation from 6.32.0 to 6.36.0

---
updated-dependencies:
- dependency-name: Marten
  dependency-version: 9.33.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: nuget-minor-patch
- dependency-name: Quartz.Extensions.Hosting
  dependency-version: 3.21.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: nuget-minor-patch
- dependency-name: Quartz.Serialization.SystemTextJson
  dependency-version: 3.21.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: nuget-minor-patch
- dependency-name: Radzen.Blazor
  dependency-version: 11.3.2
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: nuget-minor-patch
- dependency-name: WolverineFx.Marten
  dependency-version: 6.36.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: nuget-minor-patch
- dependency-name: WolverineFx.RuntimeCompilation
  dependency-version: 6.36.0
  dependency-type: direct:production
  update-type: version-update:semver-minor
  dependency-group: nuget-minor-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
@dependabot dependabot Bot added .NET Pull requests that update .NET code dependencies Pull requests that update a dependency file labels Sep 12, 2026
@dependabot @github

dependabot Bot commented on behalf of github Sep 19, 2026

Copy link
Copy Markdown
Contributor Author

Looks like these dependencies are updatable in another way, so this is no longer needed.

@dependabot dependabot Bot closed this Sep 19, 2026
@dependabot
dependabot Bot deleted the dependabot/nuget/nuget-minor-patch-f8902eea98 branch September 19, 2026 02:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file .NET Pull requests that update .NET code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants