GrayMint.Common 2.1.261

dotnet add package GrayMint.Common --version 2.1.261
                    
NuGet\Install-Package GrayMint.Common -Version 2.1.261
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="GrayMint.Common" Version="2.1.261" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="GrayMint.Common" Version="2.1.261" />
                    
Directory.Packages.props
<PackageReference Include="GrayMint.Common" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add GrayMint.Common --version 2.1.261
                    
#r "nuget: GrayMint.Common, 2.1.261"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package GrayMint.Common@2.1.261
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=GrayMint.Common&version=2.1.261
                    
Install as a Cake Addin
#tool nuget:?package=GrayMint.Common&version=2.1.261
                    
Install as a Cake Tool

GrayMint.Common

Common best practices and utilities for .NET and ASP.NET Core — async utilities, background jobs, a ready-to-go startup pipeline, EF Core helpers, and OpenAPI/Swagger integration. Battle-tested in production GrayMint services and published as small, focused NuGet packages so you can pull in exactly what you need.

NuGet License

Package What it gives you
GrayMint.Common Core utilities: recurring jobs, AsyncLock, FastDateTime, Patch<T>, typed API client base, canonical exceptions. No framework dependencies.
GrayMint.Common.AspNetCore One-call service registration and pipeline setup, JSON exception handling middleware, DI-friendly background jobs, graceful lifetime hooks.
GrayMint.Common.EntityFrameworkCore Enum lookup-table sync, safe table creation, provider-aware schema checks (SQL Server & PostgreSQL).
GrayMint.Common.Swagger NSwag, OpenAPI, and Scalar UI wired up with bearer-token auth in two calls.

All packages target net10.0 and ship under a single shared version.

dotnet add package GrayMint.Common

Highlights

Recurring jobs without the ceremony

Job runs a delegate on an interval with retry, failure counting, and shared scheduling — no Timer bookkeeping, no hosted-service boilerplate. Jobs are multiplexed onto shared JobRunner instances with bounded parallelism instead of each spinning its own timer thread.

using GrayMint.Common.Jobs;

var job = new Job(async ct => await CleanupExpiredSessions(ct),
    new JobOptions {
        Name = "session-cleanup",
        Interval = TimeSpan.FromMinutes(5),
        MaxRetry = 3
    });

In ASP.NET Core, implement IGrayMintJob and register it — the job resolves from a DI scope on every run:

public class SessionCleanupJob(AppDbContext db) : IGrayMintJob
{
    public async ValueTask RunJob(CancellationToken ct) => /* ... */;
}

builder.Services.AddGrayMintJob<SessionCleanupJob>(
    new GrayMintJobOptions { Name = "session-cleanup", Interval = TimeSpan.FromMinutes(5) });

Async primitives that hold up under load

using GrayMint.Common.Utils;

// Keyed async lock: serialize work per resource across the process
using var scope = await AsyncLock.LockAsync($"user/{userId}", ct);

// Or with a timeout instead of waiting forever
using var scope2 = await myLock.LockAsync(TimeSpan.FromSeconds(5), ct);
if (!scope2.Succeeded) return;

// Bounded parallel foreach with cancellation
await GmUtils.ForEachAsync(items, ProcessItemAsync, maxDegreeOfParallelism: 8, ct);

FastDateTime.Now / FastDateTime.UtcNow serve a cached clock with configurable precision (1s by default) — built for hot paths that read the time thousands of times per second, where DateTime.UtcNow syscall overhead actually shows up.

One Program.cs for all your services

GrayMint.Common.AspNetCore collapses the repetitive startup code every API repeats — CORS, controllers, exception handling, HTTPS redirection — into two calls with opt-in flags:

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddGrayMintCommonServices(new RegisterServicesOptions());
builder.Services.AddGrayMintSwagger(new AddSwaggerOptions { Title = "My API" });

var app = builder.Build();
app.UseGrayMintCommonServices(new UseServicesOptions());
app.UseGrayMintSwagger();

await app.Services.UseGrayMintDatabaseCommand<AppDbContext>(args); // /recreateDb, EnsureCreated
await GrayMintApp.RunAsync(app, args);                             // honors /initOnly

Exceptions that speak HTTP

Throw domain exceptions anywhere; the middleware maps them to the right status code and a consistent JSON ApiError body — NotExistsException → 404, AlreadyExistsException → 409, UnauthorizedAccessException → 403, AuthenticationException → 401. On the client side, ApiClientBase and ApiException reverse the mapping, so a typed client (e.g. NSwag-generated) can rethrow the server's original error type.

// Server: just throw
throw new NotExistsException($"Project {projectId} was not found.");

// Client: catch it as structured data
catch (ApiException ex) when (ex.Is<NotExistsException>()) { /* 404 with full ApiError */ }

Partial updates with Patch<T>

Patch<T> distinguishes "field not sent" from "field set to null" in PATCH endpoints — the classic JSON update problem:

public class UpdateUserRequest
{
    public Patch<string>? Name { get; init; }        // null → don't touch
    public Patch<string?>? Description { get; init; } // Patch(null) → clear it
}

if (request.Name != null) user.Name = request.Name;

Alongside it, Generics provides the small contract types every API ends up needing, like ListResult<T> (items + total count for paging) and IdName.

EF Core helpers

// Keep an enum lookup table in sync with its C# enum (adds, renames, removes)
await EfCoreUtil.UpdateEnums<StatusLookup, Status>(db.StatusLookups);

// Create tables idempotently; works on SQL Server and PostgreSQL
await EfCoreUtil.EnsureTablesCreated(db.Database);

// Provider-aware existence checks
bool hasFn = await EfCoreUtil.SqlFunctionExists(db.Database, "dbo", "fnCalcUsage");

// Named default constraints (SQL Server) that stay portable
builder.Property(x => x.IsEnabled)
    .HasDefaultValueWithConstraintName(true, "DF_Device_IsEnabled");

OpenAPI, NSwag & Scalar in two lines

AddGrayMintSwagger / UseGrayMintSwagger set up NSwag and/or the .NET OpenAPI generator with the Scalar UI, register a bearer-token security scheme, fix primitive-type schemas, and optionally redirect / to the docs UI — so every service exposes consistent, client-generation-ready API docs.

Development

  • src/ — the packable libraries. Shared version and package metadata come from src/Directory.Build.props; each csproj declares only its Description, TargetFramework, and references.
  • tests/ — unit tests (GrayMint.Common.Test) and a sample host (TestWebApp). Never packed.
  • pub/ — versioning and update scripts; pub/PubVersion.json is the version source of truth for all packages.
  • .github/workflows/ — every push to main builds, tests, bumps the version, and publishes to nuget.org; a daily job updates NuGet dependencies and republishes when tests pass. Nothing ships when tests fail.

Before editing files under src/GrayMint.Common, see docs/synced-classes.md — some files are refreshed as a set and should not be modified individually.

See docs/versioning-and-nuget-updates.md for how the versioning, publish, and auto-update mechanism works and how to reuse it in another repo.

dotnet build GrayMint.Common.slnx
dotnet test GrayMint.Common.slnx

License

MIT

Product 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (5)

Showing the top 5 NuGet packages that depend on GrayMint.Common:

Package Downloads
EzPin.Payment.Api

Package Description

GrayMint.Common.EntityFrameworkCore

A few utils which used in some EF Core projects.

GrayMint.Authorization.RoleManagement.Abstractions

Package Description

GrayMint.Authorization.UserManagement.Abstractions

Package Description

EzPin.Pricing.Api

Pricing API

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
2.1.261 0 7/18/2026
2.1.260 0 7/17/2026
2.1.259 0 7/17/2026
2.1.258 0 7/17/2026
2.1.257 0 7/17/2026
2.1.256 0 7/17/2026
2.1.255 0 7/17/2026
2.1.254 215 7/7/2026
2.1.253 293 6/22/2026
2.1.252 534 5/7/2026
2.1.251 222 5/7/2026
2.1.248 219 5/7/2026
2.1.247 125 5/7/2026
2.1.246 933 2/3/2026
2.1.245 346 1/1/2026
2.1.244 271 1/1/2026
2.1.243 419 12/13/2025
2.1.242 507 11/27/2025
2.1.241 336 11/16/2025
2.1.240 403 11/16/2025
Loading failed