CaeriusNet 11.0.3
dotnet add package CaeriusNet --version 11.0.3
NuGet\Install-Package CaeriusNet -Version 11.0.3
<PackageReference Include="CaeriusNet" Version="11.0.3" />
<PackageVersion Include="CaeriusNet" Version="11.0.3" />
<PackageReference Include="CaeriusNet" />
paket add CaeriusNet --version 11.0.3
#r "nuget: CaeriusNet, 11.0.3"
#:package CaeriusNet@11.0.3
#addin nuget:?package=CaeriusNet&version=11.0.3
#tool nuget:?package=CaeriusNet&version=11.0.3
CaeriusNet
<p align="center"> <a href="https://www.nuget.org/packages/CaeriusNet"><img src="https://img.shields.io/nuget/v/CaeriusNet?style=flat&logo=nuget" alt="NuGet version"></a> <a href="https://www.nuget.org/packages/CaeriusNet"><img src="https://img.shields.io/nuget/dt/CaeriusNet?style=flat" alt="NuGet downloads"></a> <img src="https://img.shields.io/badge/.NET%2010-512BD4.svg?style=flat&logo=dotnet&logoColor=white" alt=".NET 10"> <img src="https://img.shields.io/badge/C%23%2014-%23239120.svg?style=flat&logo=csharp&logoColor=white" alt="C# 14"> <img src="https://img.shields.io/badge/license-MIT-blue.svg?style=flat" alt="MIT License"> </p>
High-performance micro-ORM for C# 14 / .NET 10 that executes SQL Server Stored Procedures, maps DTOs at compile-time, passes Table-Valued Parameters, and caches results — all in a single package, zero reflection at runtime.
Installation
dotnet add package CaeriusNet
Prerequisites
- .NET 10 or later
- SQL Server 2019 or later
Quick Start
1. Configure (Program.cs)
// Standard
CaeriusNetBuilder.Create(services)
.WithSqlServer("Server=.;Database=MyDb;Trusted_Connection=True;")
.Build();
// .NET Aspire
CaeriusNetBuilder.Create(builder)
.WithAspireSqlServer("CaeriusNet")
.WithAspireRedis()
.Build();
2. Define a DTO
Source-generated (recommended):
[GenerateDto]
public sealed partial record ProductDto(int Id, string Name, decimal Price);
// Generates: ISpMapper<ProductDto> with MapFromDataReader at compile-time
Manual:
public sealed record ProductDto(int Id, string Name, decimal Price) : ISpMapper<ProductDto>
{
public static ProductDto MapFromDataReader(SqlDataReader reader)
=> new(reader.GetInt32(0), reader.GetString(1), reader.GetDecimal(2));
}
3. Execute a Stored Procedure
var sp = new StoredProcedureParametersBuilder("dbo", "sp_GetProducts", ResultSetCapacity: 1)
.AddParameter("CategoryId", categoryId, SqlDbType.Int)
.Build();
ReadOnlyCollection<ProductDto> products =
await dbContext.QueryAsReadOnlyCollectionAsync<ProductDto>(sp, ct);
Table-Valued Parameters (TVP)
Source-generated:
[GenerateTvp(Schema = "dbo", TvpName = "tvp_int")]
public sealed partial record IntTvp(int Value);
Manual:
public sealed record OrderLineDto(int ProductId, int Qty) : ITvpMapper<OrderLineDto>
{
public static string TvpTypeName => "dbo.tvp_OrderLine";
public IEnumerable<SqlDataRecord> MapAsSqlDataRecords(IEnumerable<OrderLineDto> items)
{
var meta = new[] { new SqlMetaData("ProductId", SqlDbType.Int), new SqlMetaData("Qty", SqlDbType.Int) };
foreach (var item in items)
{
var record = new SqlDataRecord(meta);
record.SetInt32(0, item.ProductId);
record.SetInt32(1, item.Qty);
yield return record;
}
}
}
Usage:
var sp = new StoredProcedureParametersBuilder("dbo", "sp_BulkInsert", ResultSetCapacity: 1)
.AddTvpParameter("OrderLines", orderLines)
.Build();
int rows = await dbContext.ExecuteNonQueryAsync(sp, ct);
Caching
var sp = new StoredProcedureParametersBuilder("dbo", "sp_GetProducts", ResultSetCapacity: 2)
.AddParameter("CategoryId", categoryId, SqlDbType.Int)
.AddFrozenCache("products:all") // immutable, process-lifetime
// .AddInMemoryCache("products:all", TimeSpan.FromMinutes(5))
// .AddRedisCache("products:all", TimeSpan.FromMinutes(5))
.Build();
Cache invalidation
Inject the ICaeriusNetCache façade to invalidate entries from any service:
public sealed class ProductsService(ICaeriusNetCache cache)
{
public async ValueTask InvalidateProductAsync(int id, CancellationToken ct)
{
await cache.RemoveAsync($"products:{id}", ct); // all tiers
await cache.RemoveAsync("products:all", CacheType.Frozen, ct);
// await cache.ClearAsync(CacheType.InMemory, ct); // dangerous; use sparingly
}
}
ClearAsync(CacheType.Redis) intentionally throws NotSupportedException — clearing a shared
distributed cache from a single service is almost never what you want.
To bound the in-memory tier, configure it explicitly at startup:
CaeriusNetBuilder.Create(services)
.WithSqlServer(connectionString)
.WithInMemoryCacheOptions(new MemoryCacheOptions { SizeLimit = 50_000 })
.Build();
When SizeLimit is set, every cached entry is sized as 1 so the limit acts as a maximum entry
count.
Transactions
Stored-procedure transactions reuse a single SqlConnection for the whole scope and attach the
underlying SqlTransaction to every command. Caching is bypassed inside a transaction.
await using var tx = await dbContext.BeginTransactionAsync(IsolationLevel.ReadCommitted, ct);
var debit = new StoredProcedureParametersBuilder("dbo", "sp_DebitAccount", ResultSetCapacity: 2)
.AddParameter("AccountId", fromId, SqlDbType.Int)
.AddParameter("Amount", amount, SqlDbType.Decimal)
.Build();
var credit = new StoredProcedureParametersBuilder("dbo", "sp_CreditAccount", ResultSetCapacity: 2)
.AddParameter("AccountId", toId, SqlDbType.Int)
.AddParameter("Amount", amount, SqlDbType.Decimal)
.Build();
await tx.ExecuteNonQueryAsync(debit, ct);
await tx.ExecuteNonQueryAsync(credit, ct);
await tx.CommitAsync(ct); // omit -> auto-rollback on dispose
Design constraints (enforced at runtime):
| Rule | Behavior |
|---|---|
| State machine | Active → Committed / RolledBack / Poisoned |
| Single in-flight command | Concurrent commands throw InvalidOperationException |
| Cache bypass | No reads from cache, no writes to cache inside a transaction |
| Poison state | A failing command poisons the scope — only RollbackAsync/DisposeAsync remain valid |
| Auto-rollback | If CommitAsync is never called, the transaction rolls back on dispose |
| No nesting | BeginTransactionAsync on an active transaction throws NotSupportedException |
Write Operations
var sp = new StoredProcedureParametersBuilder("dbo", "sp_CreateProduct", ResultSetCapacity: 2)
.AddParameter("Name", name, SqlDbType.NVarChar)
.AddParameter("Price", price, SqlDbType.Decimal)
.Build();
int rows = await dbContext.ExecuteNonQueryAsync(sp, ct);
// Or retrieve a scalar return value
int newId = await dbContext.ExecuteScalarAsync<int>(sp, ct);
Multi-Result Sets
var sp = new StoredProcedureParametersBuilder("dbo", "sp_GetDashboard", ResultSetCapacity: 0).Build();
(IEnumerable<ProductDto> products, IEnumerable<CategoryDto> categories) =
await dbContext.QueryMultipleIEnumerableAsync<ProductDto, CategoryDto>(sp, ct);
Supported up to 5 result sets: QueryMultipleIEnumerableAsync<T1,T2> through
QueryMultipleIEnumerableAsync<T1,T2,T3,T4,T5>.
Available Query Methods
| Method | Returns |
|---|---|
FirstQueryAsync<T> |
T? (first row or null) |
QueryAsReadOnlyCollectionAsync<T> |
ReadOnlyCollection<T> |
QueryAsIEnumerableAsync<T> |
IEnumerable<T> |
QueryAsImmutableArrayAsync<T> |
ImmutableArray<T> |
ExecuteNonQueryAsync |
int (rows affected) |
ExecuteAsync |
void |
ExecuteScalarAsync<T> |
T |
Supported DTO Types
The source generator maps C# types to SQL Server types and generates the correct SqlDataReader calls.
| C# Type | SQL Server Type | Reader Method |
|---|---|---|
bool |
bit |
GetBoolean |
byte |
tinyint |
GetByte |
short |
smallint |
GetInt16 |
int |
int |
GetInt32 |
long |
bigint |
GetInt64 |
decimal |
decimal |
GetDecimal |
float |
real |
GetFloat |
Half |
real |
GetFloat (cast) |
double |
float |
GetDouble |
string |
nvarchar |
GetString |
char |
nchar |
GetString (cast) |
DateTime |
datetime2 |
GetDateTime |
DateOnly |
date |
DateOnly.FromDateTime |
TimeOnly |
time |
TimeOnly.FromTimeSpan |
DateTimeOffset |
datetimeoffset |
GetDateTimeOffset |
TimeSpan |
time |
GetTimeSpan |
Guid |
uniqueidentifier |
GetGuid |
byte[] |
varbinary |
GetFieldValue<byte[]> |
| Enums | (underlying type) | (underlying reader) |
Types without a native mapping fall back to sql_variant with compile-time warning CAERIUS005.
Observability
CaeriusNet uses [LoggerMessage] source-generated structured logging with zero-allocation event methods.
| Event Range | Category |
|---|---|
| 1xxx | In-Memory cache operations |
| 2xxx | Frozen cache operations |
| 3xxx | Redis cache operations |
| 4xxx | Database / stored procedure execution |
| 5xxx | Command execution lifecycle |
All events include structured properties (cache key, schema, procedure name, elapsed time, row count) for integration
with OpenTelemetry, Seq, Application Insights, or any ILogger sink.
Tracing & Metrics (OpenTelemetry / Aspire)
CaeriusNet also publishes OpenTelemetry-compatible traces and metrics through the BCL primitives — no
OpenTelemetry SDK package is added to the library. Consumers (typically an Aspire ServiceDefaults project) opt-in
by registering the source/meter named CaeriusNet:
using CaeriusNet.Telemetry;
builder.Services.AddOpenTelemetry()
.WithTracing(t => t.AddSource(CaeriusDiagnostics.SourceName))
.WithMetrics(m => m.AddMeter(CaeriusDiagnostics.SourceName));
Spans are ActivityKind.Client, named SP {schema}.{procedure}, and tagged with the OpenTelemetry DB semantic
conventions (db.system = mssql, db.operation, db.statement) plus library-specific attributes:
caerius.sp.schema,caerius.sp.name,caerius.sp.command,caerius.sp.parameters(names only by default;CaptureParameterValuesdefaults tofalse— set it totrueonCaeriusTelemetryOptionsto also capture@name=valuepairs, but keep it disabled in production to avoid leaking PII or secrets into telemetry back-ends)caerius.tvp.used(true/false) andcaerius.tvp.type_namewhen a Table-Valued Parameter is attachedcaerius.resultset.multiandcaerius.resultset.expected_count(1 by default, 2/3/4/5 for the multi-RS overloads)caerius.cache.tier/caerius.cache.hit(set on the active span when a cache lookup occurs)caerius.tx = truewhen the call runs inside anICaeriusNetTransactioncaerius.rows_returned/caerius.rows_affectedon success
Metrics exposed by the CaeriusNet meter:
caerius.sp.duration(Histogram, ms)caerius.sp.executions(Counter)caerius.sp.errors(Counter)caerius.cache.lookups(Counter, tagged withcaerius.cache.tierandcaerius.cache.hit)
When a cache hit short-circuits the SQL call, no DB span is created — only caerius.cache.lookups{hit=true} is
emitted, so the Aspire dashboard accurately reflects that the database was not contacted.
Documentation
Full documentation, samples, and API reference: https://caerius.net
Source code & releases: https://github.com/CaeriusNET/CaeriusNet
License
MIT
| 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
- Aspire.Microsoft.Data.SqlClient (>= 13.2.4)
- Aspire.StackExchange.Redis.DistributedCaching (>= 13.2.4)
- Microsoft.Data.SqlClient (>= 7.0.1)
- Microsoft.Extensions.Caching.Memory (>= 10.0.7)
- Microsoft.Extensions.Caching.StackExchangeRedis (>= 10.0.7)
- Microsoft.Extensions.Configuration.Json (>= 10.0.7)
- Microsoft.Extensions.DependencyInjection (>= 10.0.7)
- Microsoft.Extensions.Logging.Abstractions (>= 10.0.7)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated | |
|---|---|---|---|
| 11.0.3 | 64 | 4/28/2026 | |
| 11.0.2 | 92 | 4/26/2026 | |
| 11.0.1 | 90 | 4/21/2026 | |
| 11.0.0 | 87 | 4/20/2026 | |
| 10.3.0 | 91 | 4/19/2026 | |
| 10.2.1 | 89 | 4/19/2026 | |
| 10.1.3 | 116 | 2/20/2026 | |
| 10.1.2 | 268 | 1/22/2026 | |
| 10.1.0 | 254 | 12/19/2025 | |
| 10.0.1 | 203 | 12/5/2025 | |
| 10.0.0.8-alpha | 171 | 11/2/2025 | |
| 10.0.0.7-alpha | 172 | 10/29/2025 | |
| 10.0.0.6-alpha | 164 | 10/29/2025 | |
| 10.0.0.5-alpha | 176 | 10/29/2025 | |
| 10.0.0.4-alpha | 171 | 10/28/2025 | |
| 10.0.0.3-alpha | 162 | 10/28/2025 | |
| 10.0.0.2-alpha | 166 | 10/27/2025 | |
| 10.0.0.1-alpha | 113 | 10/25/2025 | |
| 10.0.0 | 312 | 11/12/2025 | |
| 10.0.0-alpha | 249 | 10/21/2025 |
See https://github.com/CaeriusNET/CaeriusNet/releases for release notes.