Skip to content

Releases

v1.3.3

Choose a tag to compare

@ejsmith ejsmith released this 07 Jul 00:08
8a98fdb

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.Invalid and Result<T>.Invalid now accept field-keyed error messages (IEnumerable<KeyValuePair<string, string[]>>) β€” the shape produced by ASP.NET Core model validation, MiniValidator, and FluentValidation's ToDictionary() β€” so validation output passes straight through without an app-side conversion helper.
  • New ResultStatus.RateLimited with Result.RateLimited() / Result.RateLimited(message) factories on both result types. Generated endpoints map it to a 429 problem response (message becomes detail), and handler factory detection emits 429 in the endpoint OpenAPI metadata.

Middleware message replacement (#271, #279)

  • Before middleware can now return HandlerResult.ContinueWith(message) to replace the dispatched message β€” subsequent middleware, the handler, and After/Finally all receive the replacement. This is the missing piece for enriching immutable record messages (stamping tenant/user context from HttpContext), whose init-only properties can't be mutated in place; mutable messages can keep being set directly in Before.
  • Works on every dispatch path (HTTP endpoints, queues, in-process InvokeAsync); in endpoint scenarios an HttpContext? middleware parameter resolves from the call context and is null elsewhere. The middleware guide documents the enrichment patterns: abstract base record (zero per-message code β€” with on 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 SetRequestedByFilter endpoint filter is replaced by SetRequestedByMiddleware using ContinueWith with fully immutable messages, and the endpoint-filter demo now shows an HTTP-shaped concern (X-Request-Duration-Ms response-header filter).
  • The middleware guide also gained a complete MiniValidator-based validation middleware recipe built on the new Invalid overload.

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

Choose a tag to compare

@ejsmith ejsmith released this 06 Jul 21:19
b75a27e

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 fresh DbContext with 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 ScopedPerInvoke handler use its scope as their ambient scope, while targets that are themselves ScopedPerInvoke always open their own.
  • New compile-time diagnostics: FMED014 (ScopedPerInvoke on middleware β€” handler-only, falls back to Scoped), FMED015 (handler returns a deferred IQueryable<T>/IAsyncEnumerable<T> that would be enumerated after the scope is disposed), and FMED016 (FireAndForget cascades are awaited for ScopedPerInvoke handlers 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 CS9057 and the generator silently didn't run (missing handler wrappers, missing MapMediatorEndpoints).

Result factory type inference (#270)

  • Result.Created(order), Result.Ok(user), and Result.Success(user) β€” plus the message/location overloads β€” now infer the value type from the argument, so the verbose Result<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 for Created, message for Ok/Success. Use Result<string>.Created(value) when the string is the result value.

Full changelog: v1.3.1...v1.3.2

v1.3.1

Choose a tag to compare

@ejsmith ejsmith released this 15 Jun 18:04
c19b515

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 the HttpValidationProblemDetails body 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.
  • Policies and ExcludeFromDescription added 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 static BindAsync), 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)

  • *Id message 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

Choose a tag to compare

@ejsmith ejsmith released this 14 Jun 18:24
662f953

Breaking Changes

  • [HandlerEndpoint(SuccessStatusCode = …)] β†’ SuccessStatusCodes (#199) β€” the singular SuccessStatusCode (int) property was replaced with the plural SuccessStatusCodes (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. Adds MediatorResultMapperOptions<TResult> plus DI/builder helpers to override specific ResultStatus β†’ HTTP mappings without replacing the whole mapper (full IMediatorResultMapper<IResult> replacement is still supported). Result.Invalid() keeps ASP.NET Core's default ValidationProblem behavior.
  • Endpoint content-type metadata (#200) β€” AcceptsContentTypes and ProducesContentTypes on [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) returned 400.
  • Form / file upload endpoints (#233) β€” Handlers whose message exposes IFormFile, IFormFileCollection, or IFormCollection now generate multipart/form-data endpoints. 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 HandlerExcludeNamespacePatterns on [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

Choose a tag to compare

@ejsmith ejsmith released this 28 Apr 05:38

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 with CS0234: The type or namespace name 'Generated' does not exist. The using directive is now conditionally emitted only when the project has at least one handler.
  • Rename _FoundatioModule hint to .g.cs β€” The generated file now follows the .g.cs naming convention so test infrastructure properly validates it for compilation errors.

Full Changelog: v1.2.0...v1.2.1

v1.2.0

Choose a tag to compare

@ejsmith ejsmith released this 28 Apr 05:10

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 default Result<T>-to-HTTP status code mapping in generated endpoints. Register a custom mapper before AddMediator() 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

  • EndpointHttpMethod renamed to HandlerMethod β€” The HTTP method enum used in endpoint attributes has been renamed for clarity
  • HttpMethod property renamed to Method on HandlerEndpointAttribute
  • RateLimitedAttribute renamed to EndpointRateLimiterAttribute β€” More consistent naming with the endpoint convention system

Bug Fixes

  • Fix TrimStart char-array bug in TypeSymbolInfo.GetQualifiedName β€” TrimStart("global::".ToCharArray()) was stripping individual characters from the set instead of the prefix string, causing incorrect qualified names for types like long, bool, and object
  • 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/FMED018 diagnostic documentation
  • Added endpoint conventions section covering IEndpointConvention<T> interface, scoping, and deduplication
  • Clarified that endpoint generation is opt-in
  • Fixed XML docs on HandlerEndpointGroupAttribute and MediatorConfigurationAttribute

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

Choose a tag to compare

@ejsmith ejsmith released this 11 Apr 02:52

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

  • HandlerRegistry now stores rich attribute metadata, source type/method names, parameter types, and stable DescriptorId identifiers
  • New indexed lookups by attribute type and descriptor ID for extension packages and middleware
  • HandlerExecutionInfo includes an optional DescriptorId for 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 HTTP Location header on 202 responses.
  • Result.FromResult(IResult) β€” Safe cross-type conversion between Result and Result<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 Group terminology (replacing Category) for clarity and consistency
  • New RouteConventions utility encapsulating shared routing logic
  • ClaimsPrincipal is 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 (skipAuthorization parameter)
  • Notification handler activities now include the handler class name (e.g., Handle OrderHandler.OrderCreated)
  • IAuthorizationContextProvider falls back to Thread.CurrentPrincipal when HttpContext is unavailable (background workers)

Infrastructure

  • Startup logging moved from Console.WriteLine to ILoggerFactory-backed logging
  • Enabled TreatWarningsAsErrors and 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 in EventHandler were excluded from endpoint generation even when explicitly annotated. (#150)
  • Safe Result conversion β€” Generated middleware pipelines now safely convert between Result types instead of throwing InvalidCastException when the middleware returns a different IResult type 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