Migration Guides
v10.2.3 → v10.3.0
Additive changes:
PaymentRequiredExceptionmaps to the newPaymentRequiredResponse(HTTP 402)ErrorResultMapper.ToResponse(PaymentRequiredException)overload addedGlobalExceptionHandlerlogsPaymentRequiredExceptionandTooManyRequestsExceptionatWarning
No public signatures were removed or changed, so existing call sites compile without modification. The behavior change is in the exception dispatcher: before this release a PaymentRequiredException fell through to the RaBaseException arm and produced an HTTP 500 ErrorResponse; it now produces an HTTP 402 envelope.
Dependency changes
v10.3.0 depends on RA.Utilities.Api.Response 10.2.0 (new PaymentRequiredResponse), RA.Utilities.Core.Exceptions 10.2.0 (new PaymentRequiredException), and RA.Utilities.Core.Constants 10.1.1 (BaseResponseCode.PaymentRequired, BaseResponseMessages.PaymentRequired, ResponseType.PaymentRequired).
What changed
| Aspect | v10.2.3 | v10.3.0 |
|---|---|---|
PaymentRequiredException mapping | HTTP 500 ErrorResponse (generic RaBaseException arm) | HTTP 402 PaymentRequiredResponse |
ErrorResultMapper | — | New ToResponse(PaymentRequiredException) overload |
| 402/429 log level | Error | Warning |
| Success/error envelope | — | unchanged |
Migration Steps
1. No code changes required
ErrorResultResponse.Result handles the new exception type internally, and the new mapper overload is additive. GlobalExceptionHandler picks up the mapping automatically.
2. Update clients that treated 402 as a server error
If a client, retry policy, or alert currently classifies every 5xx as a server failure, review the paths that throw PaymentRequiredException: they now return 402 with responseType 18. Clients should treat 402 as a client/business outcome (paywall, exhausted quota) rather than an outage.
3. Align package versions
Reference RA.Utilities.Api 10.3.0 so the transitive RA.Utilities.Api.Response 10.2.0 and RA.Utilities.Core.Exceptions 10.2.0 dependencies are resolved together.
v10.2.1 → v10.2.2
Breaking Changes:
MiddlewareExtensionssplit intoApplicationBuilderExtensions(Use*methods) andServiceCollectionExtensions(Add*methods)
The single MiddlewareExtensions class is split into two classes that mirror the classic ASP.NET Core naming conventions: Add methods (service registration) live in ServiceCollectionExtensions, Use methods (pipeline registration) live in ApplicationBuilderExtensions. Both remain in the RA.Utilities.Api.Extensions namespace.
The extension methods themselves are unchanged — same names, same signatures. Because extension methods resolve by their target type (IServiceCollection / IApplicationBuilder), no call-site changes are required. Only code that references the class name directly (typeof(MiddlewareExtensions), static calls, unit-test naming) needs updating.
Additionally, the extension methods are refactored to C# 14 extension members (extension blocks), grouping each receiver's members in a single place. The public API surface is unchanged.
Dependency change: response models moved namespace
v10.2.2 references RA.Utilities.Api.Response 10.1.0, which moved its models from RA.Utilities.Api.Results to RA.Utilities.Api.Response. The RA.Utilities.Api public API is unchanged; update using directives only if your own code imports the response models directly:
// Before
using RA.Utilities.Api.Results;
// After
using RA.Utilities.Api.Response;
What changed
| Aspect | v10.2.1 | v10.2.2 |
|---|---|---|
Use* methods | MiddlewareExtensions | ApplicationBuilderExtensions |
Add* methods | MiddlewareExtensions | ServiceCollectionExtensions |
| Method names / signatures | — | unchanged |
| Implementation style | classic extension methods | C# 14 extension blocks |
Migration Steps
1. No code changes required for consuming projects
These are extension methods — they are resolved by the compiler based on the target type (IServiceCollection, IApplicationBuilder), not the static class name. The using directive (using RA.Utilities.Api.Extensions;) is unchanged:
// ✅ This code is unchanged in v10.2.2
using RA.Utilities.Api.Extensions;
builder.Services.AddRaExceptionHandling(); // Resolved on IServiceCollection
builder.Services.AddDefaultHeadersMiddleware(); // Resolved on IServiceCollection
builder.Services.AddLoggingMiddleware(); // Resolved on IServiceCollection
var app = builder.Build();
app.UseRaExceptionHandling(); // Resolved on IApplicationBuilder
app.UseDefaultHeadersMiddleware(); // Resolved on IApplicationBuilder
app.UseLoggingMiddleware(); // Resolved on IApplicationBuilder
app.MapEndpoints();
2. Update direct class name references (if any)
// ❌ v10.2.1
typeof(MiddlewareExtensions)
// ✅ v10.2.2
typeof(ApplicationBuilderExtensions) // Use* methods
typeof(ServiceCollectionExtensions) // Add* methods
v10.2.0 → v10.2.1
Behavior Changes:
LoggingMiddlewareresponse log is written atLogLevel.Warning(previously undocumented)LoggingMiddlewarewrites a dedicated slow-call warning entry whenWarningThresholdMillisecondsis exceededHttpRequestLogTemplate/HttpResponseLogTemplateno longer carryRequestId/TraceIdentifierproperties (viaRA.Utilities.Logging.Sharedv10.0.2)RequestContextLoggingMiddlewarerenamed toContextLoggingMiddleware(together with itsUseextension method)
The LoggingMiddleware logging behavior is now explicit:
- The request log (
HTTP Request: {@RequestLog}) is written atLogLevel.Information. - The response log (
HTTP Response: {@ResponseLog}) is written atLogLevel.Warning— and both logs are skipped entirely whenInformationlogging is not enabled. - When the total elapsed time exceeds
WarningThresholdMilliseconds, an additionalWarningentry is written alongside the response log:HTTP call to {Method} {Url} took too long ({ElapsedMilliseconds} ms).
The RequestId and TraceIdentifier correlation properties were removed from the shared log templates.
Correlation is now provided exclusively by the logging scope: every request processed by LoggingMiddleware runs inside a scope containing the x-request-id value (LoggingConstants.XRequestId), which sinks like the Serilog RequestIdEnricher (RA.Utilities.Logging.Core) pick up.
The request-context middleware type was renamed to better reflect its single responsibility: enriching the log scope with the request's X-Request-Id correlation ID. The Add extension method keeps its name; the Use method was renamed along with the type (UseRequestContextLoggingMiddleware() → UseContextLoggingMiddleware()).
What changed
| Aspect | v10.2.0 | v10.2.1 |
|---|---|---|
| Response log level | Warning (undocumented) | Warning (documented) |
| Slow-call signal | none | Additional Warning entry when WarningThresholdMilliseconds exceeded |
| Template correlation properties | RequestId, TraceIdentifier | none — via log scope |
| Middleware type | RequestContextLoggingMiddleware | ContextLoggingMiddleware |
| Registration methods | AddRequestContextLoggingMiddleware() / UseRequestContextLoggingMiddleware() | AddRequestContextLoggingMiddleware() (unchanged) / UseContextLoggingMiddleware() (renamed) |
| Behavior | X-Request-Id log scope | unchanged |
Migration Steps
No code changes are required unless you:
-
Queried
RequestId/TraceIdentifieron the log templates — the properties no longer exist. Read the correlation ID from the log scope instead (e.g.LoggingConstants.XRequestId, surfaced by the SerilogRequestIdEnricherfromRA.Utilities.Logging.Core). -
Filtered on
Informationlevel to see response logs — response logs are written atWarning; adjust level filters or alert rules accordingly. -
Referenced
RequestContextLoggingMiddlewaredirectly
No code changes are required for ordinary call sites — only direct references to the middleware type or the old Use method need updating:
// ❌ v10.2.0
app.UseMiddleware<RequestContextLoggingMiddleware>();
services.AddTransient<RequestContextLoggingMiddleware>();
app.UseRequestContextLoggingMiddleware();
// ✅ v10.2.1
app.UseMiddleware<ContextLoggingMiddleware>();
services.AddTransient<ContextLoggingMiddleware>();
app.UseContextLoggingMiddleware(); // renamed from UseRequestContextLoggingMiddleware()
3.1 Unit tests or DI assertions naming the type — update typeof(RequestContextLoggingMiddleware) to typeof(ContextLoggingMiddleware).
v10.0.6 → v10.2.0
Breaking Changes:
IEndpointredesigned into a static-abstract contractEndpointExtensions(reflection-basedAddEndpoints/MapEndpoints) removed
The instance-based IEndpoint interface (void MapEndpoint(IEndpointRouteBuilder app)) has been replaced by a static-abstract contract (static abstract string GroupName { get; } and static abstract void MapEndpoint(RouteGroupBuilder group)). Endpoint registration no longer uses reflection or DI scanning: a source generator shipped inside the package emits the app.MapEndpoints() implementation at compile time.
Why this change?
- The old pattern required reflection-based assembly scanning and DI registration at startup.
- The new pipeline validates group names at compile time (
EPMG001/EPMG002diagnostics) instead of failing at runtime. - Endpoint types no longer need to be instantiable — they are referenced statically by the generated code.
What changed
| Aspect | v10.0.6 | v10.2.0 |
|---|---|---|
| Endpoint contract | IEndpoint with void MapEndpoint(IEndpointRouteBuilder) | IEndpoint with static abstract string GroupName + static abstract void MapEndpoint(RouteGroupBuilder) |
| Group definition | — | IEndpointGroup (new) |
| Registration | builder.Services.AddEndpoints(assembly) + app.MapEndpoints() | app.MapEndpoints() only |
| Discovery | Reflection at startup | Source generator at compile time |
Migration Steps
1. Convert each IEndpoint class
// ❌ v10.0.6
public class ProductEndpoints : IEndpoint
{
public void MapEndpoint(IEndpointRouteBuilder app)
{
app.MapGet("/products", () => Results.Ok("All products"));
}
}
// ✅ v10.2.0
internal sealed class GetProductsEndpoint : IEndpoint
{
public static string GroupName => "Products";
public static void MapEndpoint(RouteGroupBuilder group)
{
group.MapGet("/", () => Results.Ok("All products"));
}
}
2. Add an IEndpointGroup per feature
// Features/Products/ProductsGroup.cs
using RA.Utilities.Api.Abstractions;
internal sealed class ProductsGroup : IEndpointGroup
{
public static string GroupName => "Products";
public static RouteGroupBuilder MapGroup(IEndpointRouteBuilder app) =>
app.MapGroup("api/products").WithTags("Products");
}
3. Remove AddEndpoints from Program.cs
// ❌ v10.0.6
builder.Services.AddEndpoints(typeof(Program).Assembly);
var app = builder.Build();
app.MapEndpoints();
// ✅ v10.2.0
var app = builder.Build();
app.MapEndpoints();
v10.0.5 → v10.0.6
Breaking Change:
- Extension methods reorganized into feature-specific classes
SuccessResponserenamed toSuccessResult
The static helper class SuccessResponse was renamed to SuccessResult to resolve a naming collision with the generic response model SuccessResponse<T>.
The generic extension classes ApplicationBuilderExtensions, ServiceCollectionExtensions, and EndpointBuilderExtensions have been replaced with feature-specific alternatives.
All classes remain in the same namespace (RA.Utilities.Api.Extensions).
Why this change?
- Having both
SuccessResponse(static helper class) andSuccessResponse<T>(generic response model) in the same namespace caused ambiguity and IDE confusion. SuccessResultbetter describes what the class does — it createsIResultobjects.- The old naming (
ApplicationBuilderExtensions,ServiceCollectionExtensions) was too generic — each class held only one method and provided no hint about what feature it related to. The new organization follows the pattern established byExceptionHandlingExtensions, where each file is a self-contained feature with itsAdd/Usepair.
What changed
| Aspect | v10.0.5 | v10.0.6 |
|---|---|---|
AddLoggingMiddleware location | ServiceCollectionExtensions | HttpLoggingExtensions |
UseLoggingMiddleware location | ApplicationBuilderExtensions | HttpLoggingExtensions |
Validate<TModel> location | EndpointBuilderExtensions | ValidationExtensions |
AddEndpoints location | EndpointExtensions | EndpointExtensions (unchanged) |
MapEndpoints location | EndpointExtensions | EndpointExtensions (unchanged) |
AddRaExceptionHandling location | RaExceptionHandlingExtensions | ExceptionHandlingExtensions |
UseRaExceptionHandling location | RaExceptionHandlingExtensions | ExceptionHandlingExtensions |
AddDefaultHeadersMiddleware location | — | DefaultHeadersMiddlewareExtensions (new) |
UseDefaultHeadersMiddleware location | — | DefaultHeadersMiddlewareExtensions (new) |
AddRequestContextLoggingMiddleware location | — | RequestContextLoggingMiddlewareExtensions (new) |
UseRequestContextLoggingMiddleware location | — | RequestContextLoggingMiddlewareExtensions (new) |
Migration Steps
1. No code changes required for consuming projects
These are extension methods — they are resolved by the compiler based on the target type (IServiceCollection, IApplicationBuilder, RouteHandlerBuilder, WebApplication), not the static class name. The using directive (using RA.Utilities.Api.Extensions;) is unchanged.
Your existing Program.cs code continues to compile without modification:
// ✅ This code is unchanged in v10.0.6
using RA.Utilities.Api.Extensions;
builder.Services.AddLoggingMiddleware(); // Resolved on IServiceCollection
builder.Services.AddEndpoints(typeof(Program).Assembly); // Resolved on IServiceCollection
builder.Services.AddRaExceptionHandling(); // Resolved on IServiceCollection
var app = builder.Build();
app.UseLoggingMiddleware(); // Resolved on IApplicationBuilder
app.UseRaExceptionHandling(); // Resolved on IApplicationBuilder
app.MapEndpoints(); // Resolved on WebApplication
2. Rename all references
// ❌ v10.0.5
using RA.Utilities.Api.Mapper;
return SuccessResponse.Ok(product);
// ✅ v10.0.6
using RA.Utilities.Api.Mapper;
return SuccessResult.Ok(product);
3. Response Models Extracted to a Standalone Package
The response model types are published in the standalone RA.Utilities.Api.Response package again. RA.Utilities.Api depends on it, so existing projects receive the types transitively — no package changes are required. To reference the models directly:
dotnet add package RA.Utilities.Api.Response
With RA.Utilities.Api.Response v10.1.0+, the types live in the RA.Utilities.Api.Response namespace. Replace using RA.Utilities.Api.Results; with using RA.Utilities.Api.Response;. See the Api.Response migration guide for the full before/after steps.
4. Update direct class name references (if any)
If your code directly references the old class names (unlikely for extension methods, but possible in unit tests or custom reflection code), update them:
// ❌ Old class names (removed)
typeof(ServiceCollectionExtensions)
typeof(ApplicationBuilderExtensions)
typeof(EndpointBuilderExtensions)
// ✅ New class names
typeof(HttpLoggingExtensions)
typeof(ValidationExtensions)
// EndpointExtensions is unchanged
5. Test files
If you have test files named after the old classes, rename them for clarity:
| Old test class | New test class |
|---|---|
EndpointBuilderExtensionsTests | ValidationExtensionsTests |
Full Changelog
See the package release notes for a complete version history.