Repository navigation
Releases
v1.3.3
Migration ergonomics driven by a large production migration from MassTransit's mediator (#271). All changes are additive and backward-compatible with 1.3.2.
Result ergonomics (#271, #279)
Result.InvalidandResult<T>.Invalidnow accept field-keyed error messages (IEnumerable<KeyValuePair<string, string[]>>) β the shape produced by ASP.NET Core model validation, MiniValidator, and FluentValidation'sToDictionary()β so validation output passes straight through without an app-side conversion helper.- New
ResultStatus.RateLimitedwithResult.RateLimited()/Result.RateLimited(message)factories on both result types. Generated endpoints map it to a 429 problem response (message becomesdetail), and handler factory detection emits 429 in the endpoint OpenAPI metadata.
Middleware message replacement (#271, #279)
Beforemiddleware can now returnHandlerResult.ContinueWith(message)to replace the dispatched message β subsequent middleware, the handler, andAfter/Finallyall receive the replacement. This is the missing piece for enriching immutable record messages (stamping tenant/user context fromHttpContext), whose init-only properties can't be mutated in place; mutable messages can keep being set directly inBefore.- Works on every dispatch path (HTTP endpoints, queues, in-process
InvokeAsync); in endpoint scenarios anHttpContext?middleware parameter resolves from the call context and isnullelsewhere. The middleware guide documents the enrichment patterns: abstract base record (zero per-message code βwithon the base type preserves the derived runtime type), interface with a one-line self-copy method, or a plain settable property. - The CleanArchitectureSample dogfoods the pattern: the
SetRequestedByFilterendpoint filter is replaced bySetRequestedByMiddlewareusingContinueWithwith fully immutable messages, and the endpoint-filter demo now shows an HTTP-shaped concern (X-Request-Duration-Msresponse-header filter). - The middleware guide also gained a complete MiniValidator-based validation middleware recipe built on the new
Invalidoverload.
Fixed
- Re-pinned Microsoft.CodeAnalysis to 5.0.0 (#280) after dependency bumps reverted the #269 downgrade on main β a generator built against Roslyn 5.3.0 silently fails to load (CS9057) on .NET 10 SDKs older than 10.0.3xx. The regression never shipped in a release; dependabot now ignores the load-bearing CodeAnalysis packages so future bumps are manual and verified against SDK 10.0.1xx.
Dependency updates
- Foundatio and Foundatio.Xunit.v3 13.0.2 (stable), Scalar.AspNetCore 2.16.6, Verify.DiffPlex 3.3.0, WolverineFx 6.16.0 (benchmarks) (#272β#278).
Full changelog: v1.3.2...v1.3.3
v1.3.2
Scope-per-invoke handler isolation, broader .NET 10 SDK compatibility, and Result factory ergonomics. All changes are additive and backward-compatible with 1.3.1.
Scope-per-invoke handler isolation (#268)
- New
MediatorLifetime.ScopedPerInvokeβ the mediator opens a fresh DI scope for each invocation, resolves the entire pipeline (middleware + handler + their dependencies) from it, and disposes the scope when the invocation β including cascading messages β completes. This matches the scope-per-message semantics of MassTransit's mediator and Wolverine, for handlers that assume isolated scoped dependencies per dispatch (e.g. a freshDbContextwith its own change tracker). Requested in #267. - Apply per handler with
[Handler(Lifetime = MediatorLifetime.ScopedPerInvoke)], or assembly-wide with[assembly: MediatorConfiguration(HandlerLifetime = MediatorLifetime.ScopedPerInvoke)]with per-handler opt-out β the natural path when migrating a project off MassTransit mediator or Wolverine. Ambient dispatch (caller-owned scopes) remains the default; nothing changes for existing code. - Scopes follow the dispatch tree: nested and cascading dispatches from a
ScopedPerInvokehandler use its scope as their ambient scope, while targets that are themselvesScopedPerInvokealways open their own. - New compile-time diagnostics:
FMED014(ScopedPerInvokeon middleware β handler-only, falls back toScoped),FMED015(handler returns a deferredIQueryable<T>/IAsyncEnumerable<T>that would be enumerated after the scope is disposed), andFMED016(FireAndForgetcascades are awaited forScopedPerInvokehandlers so background dispatches can't outlive the scope).
Works on every .NET 10 SDK (#269)
- The source generator and code fixes now reference Microsoft.CodeAnalysis 5.0.0 (previously 5.3.0), so the package works with SDK 10.0.100 and later instead of requiring 10.0.3xx+. Previously, building with an older SDK produced warning
CS9057and the generator silently didn't run (missing handler wrappers, missingMapMediatorEndpoints).
Result factory type inference (#270)
Result.Created(order),Result.Ok(user), andResult.Success(user)β plus the message/location overloads β now infer the value type from the argument, so the verboseResult<OrderView>.Created(order)spelling is no longer required. Generated endpoint status-code detection (e.g..Produces<T>(201)) picks the new overloads up unchanged.- Note for
Result<string>: a string argument still binds to the existing non-generic overloads β location forCreated, message forOk/Success. UseResult<string>.Created(value)when the string is the result value.
Full changelog: v1.3.1...v1.3.2
v1.3.1
Endpoint generation improvements. All changes are additive and backward-compatible with 1.3.0.
Endpoint metadata options (#234)
[HandlerEndpoint(ExcludeFromDescription = true)]β hide an endpoint from OpenAPI/API explorers while keeping it routable.[HandlerEndpoint(DisplayName = "...")]β pin the ASP.NET Core endpoint display name (logs/diagnostics).- Auto-detected
Result.Invalid()status codes are now documented with.ProducesValidationProblem()(matching theHttpValidationProblemDetailsbody the mapper returns). MediatorResultMapperOptions<TResult>.MapValue<TValue>(...)β value-shape result mapping (with an optional predicate), evaluated before status mappers.
Named endpoint groups (#238)
- New assembly-level
[MediatorEndpointGroup(Name, RoutePrefix, Tags, EndpointFilters, Policies, ExcludeFromDescription)], referenced by[HandlerEndpoint(Group = "...")], so handlers across classes share one route prefix, tags, filters, and authorization policies under a single group. PoliciesandExcludeFromDescriptionadded to the class-level[HandlerEndpointGroup]as well.
Custom request-body binding (#238)
- The generator now omits
[FromBody]when the message type defines its own minimal-API binding (IBindableFromHttpContext<TSelf>or a staticBindAsync), so ASP.NET Core's custom binding is no longer preempted β enabling non-standard bodies (JSON Patch, legacy partial JSON, XML) through platform seams.
Binding fix (#236)
*Idmessage properties are no longer bound as route/query parameters for body commands with an explicit route that lacks the matching placeholder. Use[FromBody]on the message parameter to force whole-message body binding.
Full changelog: v1.3.0...v1.3.1
v1.3.0
Breaking Changes
[HandlerEndpoint(SuccessStatusCode = β¦)]βSuccessStatusCodes(#199) β the singularSuccessStatusCode(int) property was replaced with the pluralSuccessStatusCodes(int[]). Update any usages.
Features
- Endpoint success metadata & result-mapping options (#199) β Success result factories (
Result.Created(),Result.Accepted(),Result.NoContent()) are detected and emit matching response metadata. AddsMediatorResultMapperOptions<TResult>plus DI/builder helpers to override specificResultStatus β HTTPmappings without replacing the whole mapper (fullIMediatorResultMapper<IResult>replacement is still supported).Result.Invalid()keeps ASP.NET Core's defaultValidationProblembehavior. - Endpoint content-type metadata (#200) β
AcceptsContentTypesandProducesContentTypeson[HandlerEndpoint]generate.Accepts<T>(...)/.Produces<T>(...)OpenAPI metadata. - Explicit route-template parameter binding (#232) β A
{placeholder}in an explicit[HandlerEndpoint(..., "Path/{param}")]template now binds from the route regardless of property naming or HTTP method. Previously the value was emitted as[FromQuery], so path-only requests (e.g.GET /contracts/H-42) returned400. - Form / file upload endpoints (#233) β Handlers whose message exposes
IFormFile,IFormFileCollection, orIFormCollectionnow generatemultipart/form-dataendpoints. File properties bind by name, other fields bind via[FromForm], route placeholders still bind from the route. Antiforgery (CSRF) validation stays required by default β disable it explicitly per endpoint with[HandlerEndpoint(DisableAntiforgery = true)]or assembly-wide with[assembly: MediatorConfiguration(EndpointDisableAntiforgery = true)]. - Exclude handlers by namespace β new
HandlerExcludeNamespacePatternson[assembly: MediatorConfiguration]excludes handlers in matching namespaces from generation and discovery (exact match or trailing-wildcard prefix, e.g."MyCompany.Messaging.*").
Dependencies
- Numerous dependency updates, including WolverineFx (5.31.1 β 6.3.2), MassTransit (9.0.1 β 9.1.1), Immediate.Handlers, Scalar.AspNetCore, Scriban, Verify.XunitV3, and the Microsoft.Extensions.* / Microsoft.AspNetCore.* packages. See the full changelog.
Full Changelog: v1.2.1...v1.3.0
v1.2.1
Bug Fixes
- Fix compilation error in middleware-only projects β The source generator emitted
using Foundatio.Mediator.Generated;unconditionally in_FoundatioModule, but that namespace only exists when handler wrapper types are generated. Projects containing only middleware (no handlers) failed withCS0234: The type or namespace name 'Generated' does not exist. Theusingdirective is now conditionally emitted only when the project has at least one handler. - Rename
_FoundatioModulehint to.g.csβ The generated file now follows the.g.csnaming convention so test infrastructure properly validates it for compilation errors.
Full Changelog: v1.2.0...v1.2.1
v1.2.0
What's New
Endpoint Conventions & Customization
IEndpointConvention<TBuilder>β New interface for compile-time endpoint customization via attributes. Convention attributes on methods, classes, or assemblies configure endpoint/group builders at startup without runtime reflection. Supports 3-level precedence with most-derived-wins deduplication (method overrides class overrides assembly). (#176)- Customizable result-to-HTTP mapping β New
IMediatorResultMapper<TResult>interface allows overriding the defaultResult<T>-to-HTTP status code mapping in generated endpoints. Register a custom mapper beforeAddMediator()to override the defaults. (#177)
Route Constraints
- Auto-generated route constraints β Route parameters now automatically include type constraints based on the message property type (e.g.,
{orderId:guid},{itemId:long},{widgetId:int}). Supported types:int,long,guid,bool,datetime,decimal,double,float. String parameters remain unconstrained.
Endpoint Attribute Improvements
- Route shorthand constructor β
[HandlerEndpoint("{productId}")]instead of[HandlerEndpoint(Route = "{productId}")] - Positional constructor β
[HandlerEndpoint(HandlerMethod.Get, "/{productId}")]for concise explicit endpoint declarations HandlerMethod-only constructor β[HandlerEndpoint(HandlerMethod.Get)]for endpoints with no additional route segment beyond the group prefix[StringSyntax("Route")]annotation β Route parameters on[HandlerEndpoint]now have IDE route-completion support (with a netstandard2.0 polyfill)
"Make Endpoint Explicit" Code Fix
- New code fix for
FMED017β Offers "Make endpoint explicit" and "Make all endpoints in class explicit" actions that insert[HandlerEndpoint]and[HandlerEndpointGroup]attributes to freeze convention-based routes so they won't change across library versions
Breaking Changes
EndpointHttpMethodrenamed toHandlerMethodβ The HTTP method enum used in endpoint attributes has been renamed for clarityHttpMethodproperty renamed toMethodonHandlerEndpointAttributeRateLimitedAttributerenamed toEndpointRateLimiterAttributeβ More consistent naming with the endpoint convention system
Bug Fixes
- Fix
TrimStartchar-array bug inTypeSymbolInfo.GetQualifiedNameβTrimStart("global::".ToCharArray())was stripping individual characters from the set instead of the prefix string, causing incorrect qualified names for types likelong,bool, andobject - Support non-record message types in endpoint generation β GET/DELETE constructor-binding now uses object initializer syntax for class types instead of record-only named constructor args; skip
[AsParameters]binding for zero-property message types - Fix nullable warning in RetryMiddleware sample β
GetPolicy()now falls back to the default policy when the named policy is not found
Documentation
- Added locking routes section and
FMED017/FMED018diagnostic documentation - Added endpoint conventions section covering
IEndpointConvention<T>interface, scoping, and deduplication - Clarified that endpoint generation is opt-in
- Fixed XML docs on
HandlerEndpointGroupAttributeandMediatorConfigurationAttribute
Dependencies
- Foundatio packages updated to 13.0.0
- Microsoft.SourceLink.GitHub 10.0.203
- .NET 10.0.7 package updates (BCL, AspNetCore, DI, Hosting, Diagnostics)
- Scalar.AspNetCore 2.14.6
Full Changelog: v1.1.0...v1.2.0
v1.1.0
What's New
IMediatorBuilder Fluent API
AddMediator() now returns IMediatorBuilder instead of IServiceCollection, enabling fluent chaining for extension packages:
services.AddMediator().AddQueues();CallContext & HTTP Binding
CallContextβ A new pooled, lightweight mechanism for passing request-scoped objects (HttpContext,HttpRequest,HttpResponse) to handler and middleware methods, reducing allocations and improving testability- HTTP binding attributes β Message properties can now use
[FromHeader],[FromQuery], and[FromRoute]to bind values from the HTTP request before the handler is invoked
Handler Metadata & Registry
HandlerRegistrynow stores rich attribute metadata, source type/method names, parameter types, and stableDescriptorIdidentifiers- New indexed lookups by attribute type and descriptor ID for extension packages and middleware
HandlerExecutionInfoincludes an optionalDescriptorIdfor improved traceability
MessageContext<T> Subscriptions
mediator.SubscribeAsync<MessageContext<T>>() wraps subscription messages with publisher metadata (ActivityContext), enabling trace context propagation across publish/subscribe boundaries.
Result Type Improvements
Result.Accepted(message, location)β New overload that accepts a location string (e.g., a job status URL). Generated endpoints now emit a proper HTTPLocationheader on 202 responses.Result.FromResult(IResult)β Safe cross-type conversion betweenResultandResult<T>, replacing hard casts in generated middleware pipelines
Endpoint Generation Enhancements
- Improved route generation with better handling of non-CRUD verbs and kebab-case conversion
- Refactored to use
Groupterminology (replacingCategory) for clarity and consistency - New
RouteConventionsutility encapsulating shared routing logic ClaimsPrincipalis now automatically available as a handler parameter in endpoints- Assembly name included in endpoint mapping log messages for modular apps
Authorization & OpenTelemetry
- Generated handlers skip redundant authorization for event/notification dispatch (
skipAuthorizationparameter) - Notification handler activities now include the handler class name (e.g.,
Handle OrderHandler.OrderCreated) IAuthorizationContextProviderfalls back toThread.CurrentPrincipalwhenHttpContextis unavailable (background workers)
Infrastructure
- Startup logging moved from
Console.WriteLinetoILoggerFactory-backed logging - Enabled
TreatWarningsAsErrorsand fixed all nullable reference type annotations - New endpoint benchmarks comparing hand-coded minimal APIs vs source-generated endpoints
- Updated to .NET 10.0.5 dependencies
Bug Fixes
- [HandlerEndpoint] override β An explicit
[HandlerEndpoint]attribute now correctly overrides the event handler auto-exclude heuristic. Previously, handlers with class names ending inEventHandlerwere excluded from endpoint generation even when explicitly annotated. (#150) - Safe Result conversion β Generated middleware pipelines now safely convert between
Resulttypes instead of throwingInvalidCastExceptionwhen the middleware returns a differentIResulttype than the handler declares
Contributors
Thanks to all contributors and the Dependabot for keeping dependencies up to date!
Full Changelog: v1.0.1...v1.1.0