SyntaxCircus.Common
0.2.0
dotnet add package SyntaxCircus.Common --version 0.2.0
NuGet\Install-Package SyntaxCircus.Common -Version 0.2.0
<PackageReference Include="SyntaxCircus.Common" Version="0.2.0" />
<PackageVersion Include="SyntaxCircus.Common" Version="0.2.0" />
<PackageReference Include="SyntaxCircus.Common" />
paket add SyntaxCircus.Common --version 0.2.0
#r "nuget: SyntaxCircus.Common, 0.2.0"
#:package SyntaxCircus.Common@0.2.0
#addin nuget:?package=SyntaxCircus.Common&version=0.2.0
#tool nuget:?package=SyntaxCircus.Common&version=0.2.0
SyntaxCircus.Common
The handful of contract types and dependency-free helpers that keep getting reinvented per product: operation results, a pagination result, ClaimsPrincipal claim resolution, a periodic background service base, and a standalone sliding-window rate limiter for hosts that aren't a normal ASP.NET Core pipeline.
Since 0.2.0 the package has no web framework dependency: it references only Microsoft.Extensions.* abstractions, so console, worker, SDK and MAUI consumers do not inherit Microsoft.AspNetCore.App.
No support guaranteed. Published as-is and maintained on a best-effort basis. Issues and PRs are welcome, but there's no SLA — fork it or vendor what you need if that's not enough.
Result and Result<T>
Use transport-neutral results across application boundaries. Errors carry a stable code, a client-safe message, a semantic kind, and an optional validation target; Result/Result<T> themselves do not carry HTTP status codes — see ApiResult/ApiResult<T> below for the one case that needs to.
public async Task<Result<Widget>> HandleAsync(CreateWidgetRequest request)
{
if (string.IsNullOrWhiteSpace(request.Name))
{
return Result<Widget>.Failure(new ResultError(
"name-required",
"A name is required.",
ResultErrorKind.Validation,
"name"));
}
var widget = await CreateAsync(request);
return Result<Widget>.Success(widget);
}
Failures contain at least one error. Multiple errors are reserved for validation failures, and all errors in a result have the same kind. Accessing Value on a failed Result<T> throws.
ApiResult and ApiResult<T>
For the rare case where a failure needs to carry an exact upstream HTTP
status code — proxying a third-party API's 429/502/503 rather than
collapsing it into one of ResultErrorKind's fixed kinds — ApiResult
and ApiResult<T> extend Result/Result<T> with a StatusCode:
public async Task<ApiResult<Widget>> HandleAsync(CreateWidgetRequest request)
{
var upstream = await CallUpstreamApiAsync(request);
if (!upstream.IsSuccess)
{
return ApiResult<Widget>.Failure(
upstream.StatusCode,
"upstream-widget-error",
"The upstream widget service returned an error.");
}
return ApiResult<Widget>.Success(upstream.Widget);
}
StatusCode is null on success — success-side status selection stays
the caller's responsibility, same as Result/Result<T>. Failure
requires a status in the 400–599 range. The constructed error always has
ResultErrorKind.Passthrough and no validation target.
PagedResult<T>
new PagedResult<Widget>(items, page: 1, pageSize: 25, totalCount: 142);
// .TotalPages, .HasPreviousPage, .HasNextPage are computed
ClaimsPrincipalExtensions
user.GetSubject(); // "sub" claim, falling back to ClaimTypes.NameIdentifier
user.GetEmail(); // "email" claim, falling back to ClaimTypes.Email
user.GetDisplayName(); // "name" claim, falling back to "preferred_username"
Moved: ICurrentUserService
ICurrentUserService, its implementation and AddCurrentUserService() live in SyntaxCircus.AspNetCore.Common 0.1.16+ (namespace SyntaxCircus.AspNetCore.Common) since 0.2.0.
PeriodicBackgroundService
public sealed class CleanupWorker(ILogger<CleanupWorker> logger)
: PeriodicBackgroundService(TimeSpan.FromMinutes(5), logger)
{
protected override async Task ExecuteTickAsync(CancellationToken cancellationToken)
{
// do the periodic work
}
}
A BackgroundService base that runs ExecuteTickAsync on a fixed interval — one failing tick is caught and logged rather than crashing the whole service, and the delay is between ticks (not tick starts), so a slow tick can't overlap the next one.
SlidingWindowRateLimiter
var limiter = new SlidingWindowRateLimiter(permitLimit: 5, window: TimeSpan.FromMinutes(1));
if (!limiter.TryAcquire(key: remoteIpAddress))
{
// reject
}
A plain, key-based sliding-window limiter with no HttpContext or middleware dependency — for hosts that aren't a normal ASP.NET Core request pipeline (an embedded server, a SignalR hub, a background worker) where System.Threading.RateLimiting's middleware integration doesn't apply.
Contributing
Since 0.2.0 this package has no web framework dependency, and ICurrentUserService moved to SyntaxCircus.AspNetCore.Common. The web-neutral contracts proposal is kept as history.
Issues and pull requests are welcome:
- Keep changes focused, with a clear description of the behavior change.
- Match the existing code style (see
.editorconfig). - Call out any breaking changes to the public API in your PR description.
License
MIT — see LICENSE.txt.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Microsoft.Extensions.Hosting.Abstractions (>= 10.0.12)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.12)
NuGet packages (6)
Showing the top 5 NuGet packages that depend on SyntaxCircus.Common:
| Package | Downloads |
|---|---|
|
SyntaxCircus.AspNetCore.Common
Small, near-universal ASP.NET Core host boilerplate: result-to-ProblemDetails mapping, correlation-ID middleware, security headers, exception handling, and a current-user service. |
|
|
SyntaxCircus.AspNetCore.Common.MassTransit
Optional companion to SyntaxCircus.AspNetCore.Common: MassTransit consume/publish/send filters that propagate the configured correlation ID across message-bus boundaries, keeping log enrichment consistent with the HTTP middleware. |
|
|
SyntaxCircus.Cmsify.Core
Cmsify domain entities, validation, service contracts, and direct workspace request handlers. |
|
|
SyntaxCircus.Cmsify.Infrastructure
Cmsify PostgreSQL persistence and migrations, repositories, storage, host integration, audit, and background services. |
|
|
SyntaxCircus.Cmsify.Infrastructure.Sqlite
Optional SQLite persistence registration and schema-only migrations for Cmsify. |
GitHub repositories
This package is not used by any popular GitHub repositories.