CaeriusNet is a high-performance micro-ORM for C# 14 and .NET 10, built exclusively around SQL Server stored procedures. It eliminates boilerplate data-access code through compile-time source generators, delivers multiple caching strategies out of the box, and supports multi-result-set queries — all in a single NuGet package.
| Directory | Description |
|---|---|
Src/ |
Core library — CaeriusNet NuGet package source |
SourceGenerators/ |
Roslyn incremental source generators and analyzers used by the package |
Tests/ |
Unit tests, generator tests, integration tests |
Benchmark/ |
BenchmarkDotNet performance benchmarks |
Exemples/ |
Example projects (Aspire + Console); directory name retained for compatibility |
Documentations/ |
VitePress documentation site |
- Zero reflection at runtime — DTO and TVP mappers are generated at compile time via Roslyn source generators.
- Stored procedures first — every query is a stored procedure call; no inline SQL, no query builders.
- Three caching layers — Frozen (immutable in-process), In-Memory (TTL), and Redis (distributed).
- Multi-result sets — retrieve up to 5 strongly-typed result sets in a single database round-trip.
- TVP support — pass structured data to SQL Server efficiently without any DataTable overhead.
- AutoContracts — optionally pull and verify stored procedure contracts during
dotnet buildfrom the same NuGet package. - Aspire-ready — first-class integration with .NET Aspire for cloud-native scenarios.
dotnet add package CaeriusNetThe runtime, analyzers, source generators, and AutoContracts MSBuild integration are bundled in this single package.
1. Register the services
CaeriusNetBuilder
.Create(services)
.WithSqlServer(connectionString)
.Build();2. Define a DTO
[GenerateDto]
public sealed partial record UserDto(int Id, string Name, byte Age);3. Execute a stored procedure
var sp = new StoredProcedureParametersBuilder("dbo", "usp_GetUsers_By_Age", 250)
.AddParameter("Age", age, SqlDbType.Int)
.AddInMemoryCache("users:age:" + age, TimeSpan.FromMinutes(5))
.Build();
var users = await dbContext.QueryAsReadOnlyCollectionAsync<UserDto>(sp, cancellationToken);| Concept | Description |
|---|---|
| Stored procedures first | All queries target SQL Server stored procedures. Inline SQL is not supported. |
| Compile-time mapping | [GenerateDto] and [GenerateTvp] generate ISpMapper<T> and ITvpMapper<T> at build time. |
| TVP support | Pass structured data as Table-Valued Parameters using .AddTvpParameter(). |
| Caching layers | Choose Frozen, In-Memory, or Redis caching per query — or combine them. |
| Multi-result sets | Call QueryMultipleIEnumerableAsync<T1, T2, …> to retrieve up to 5 result sets at once. |
You can map result sets either automatically with source generation or manually by implementing the interface.
[GenerateDto]
public sealed partial record UserDto(int Id, string Name, byte Age);The [GenerateDto] attribute instructs the compiler to generate the ISpMapper<UserDto> implementation automatically.
public sealed record UserDto(int Id, string Name, byte Age) : ISpMapper<UserDto>
{
public static UserDto MapFromDataReader(SqlDataReader reader)
=> new(reader.GetInt32(0), reader.GetString(1), reader.GetByte(2));
}Use [GenerateTvp] to generate a TVP mapper that matches an existing SQL Server user-defined table type.
1. Define the SQL type
CREATE TYPE dbo.tvp_int AS TABLE (Id INT NOT NULL);2. Generate the C# mapper
[GenerateTvp(Schema = "dbo", TvpName = "tvp_int")]
public sealed partial record UsersIdsTvp(int Id);3. Use it in a query
var sp = new StoredProcedureParametersBuilder("dbo", "sp_GetUsers_By_Ids", 1024)
.AddTvpParameter("Ids", tvpItems)
.Build();
var users = await dbContext.QueryAsIEnumerableAsync<UserDto>(sp, cancellationToken);Retrieve multiple strongly-typed result sets in a single round-trip using QueryMultipleIEnumerableAsync.
The multi-result APIs follow the same ValueTask async shape as the rest of the read/write API.
var sp = new StoredProcedureParametersBuilder("dbo", "sp_GetDashboard", 512).Build();
var (users, orders) = await dbContext.QueryMultipleIEnumerableAsync<UserDto, OrderDto>(
sp, cancellationToken);You can query up to five result sets: QueryMultipleIEnumerableAsync<T1, T2, T3, T4, T5>.
Add a cache policy directly on the StoredProcedureParametersBuilder. All three strategies are opt-in per query.
var sp = new StoredProcedureParametersBuilder("dbo", "usp_GetConfig", 64)
.AddFrozenCache("config:all")
.Build();var sp = new StoredProcedureParametersBuilder("dbo", "usp_GetUsers_By_Age", 250)
.AddParameter("Age", age, SqlDbType.Int)
.AddInMemoryCache("users:age:" + age, TimeSpan.FromMinutes(5))
.Build();var sp = new StoredProcedureParametersBuilder("dbo", "usp_GetProducts", 512)
.AddRedisCache("products:all", TimeSpan.FromMinutes(10))
.Build();To enable Redis, register it during setup:
CaeriusNetBuilder
.Create(services)
.WithSqlServer(connectionString)
.WithRedis("localhost:6379")
.Build();Execute multiple stored procedures atomically within a single transaction scope.
await using var tx = await dbContext.BeginTransactionAsync(IsolationLevel.ReadCommitted, ct);
var sp1 = new StoredProcedureParametersBuilder("dbo", "sp_DebitAccount")
.AddParameter("AccountId", fromId, SqlDbType.Int)
.AddParameter("Amount", amount, SqlDbType.Decimal)
.Build();
var sp2 = new StoredProcedureParametersBuilder("dbo", "sp_CreditAccount")
.AddParameter("AccountId", toId, SqlDbType.Int)
.AddParameter("Amount", amount, SqlDbType.Decimal)
.Build();
await tx.ExecuteNonQueryAsync(sp1, ct);
await tx.ExecuteNonQueryAsync(sp2, ct);
await tx.CommitAsync(ct);Key design points:
- State machine — Active → Committed / RolledBack / Poisoned.
- Single in-flight command — concurrent commands on the same transaction throw
InvalidOperationException. - Cache bypass — no reads from cache, no writes to cache inside a transaction.
- Auto-rollback on dispose — if
CommitAsyncis never called, the transaction rolls back.
Use the execute methods for INSERT, UPDATE, DELETE, and scalar return values.
var sp = new StoredProcedureParametersBuilder("dbo", "sp_UpdateUser")
.AddParameter("Guid", guid, SqlDbType.UniqueIdentifier)
.AddParameter("Age", age, SqlDbType.TinyInt)
.Build();
int rows = await dbContext.ExecuteNonQueryAsync(sp, cancellationToken);var sp = new StoredProcedureParametersBuilder("dbo", "sp_CreateUser")
.AddParameter("Name", name, SqlDbType.NVarChar)
.Build();
int newId = await dbContext.ExecuteScalarAsync<int>(sp, cancellationToken);| Category | Method |
|---|---|
| Read | FirstQueryAsync, QueryAsReadOnlyCollectionAsync, QueryAsIEnumerableAsync, QueryAsImmutableArrayAsync |
| Write | ExecuteNonQueryAsync, ExecuteAsync, ExecuteScalarAsync<T> |
| Multi-result | QueryMultipleIEnumerableAsync<T1,T2> … <T1,T2,T3,T4,T5> |
Use WithAspireSqlServer and WithAspireRedis when running under .NET Aspire.
CaeriusNetBuilder.Create(builder)
.WithAspireSqlServer("CaeriusNet")
.WithAspireRedis()
.Build();| Requirement | Minimum version |
|---|---|
| .NET | 10 |
| C# | 14 |
| SQL Server | 2019 |
Before publishing, validate the NuGet package contents and smoke-test a consumer project:
pwsh ./.github/scripts/ValidatePackage.ps1 -Configuration Release -OutputDirectory .work/package-validationTrim/AOT analyzer validation is covered by the Release build. To run it directly:
dotnet build Src/CaeriusNet.csproj --configuration Release -p:IsAotCompatible=true -p:EnableTrimAnalyzer=true- 📖 Full Documentation — VitePress docs with guides, API reference, benchmarks
- 🚀 Getting Started — Installation and first query
- 📊 Benchmarks — Performance measurements with BenchmarkDotNet
- 🔧 Source Generators —
[GenerateDto]and[GenerateTvp] - 🏗️ .NET Aspire — Cloud-native integration
Contributions are welcome. Read these first:
CONTRIBUTING.md— coding standards, branch model, conventional commits, build/test commands.CODE_OF_CONDUCT.md— Contributor Covenant 2.1.SECURITY.md— how to responsibly report vulnerabilities (private GitHub advisory).SUPPORT.md— where to ask questions and get help.
Quick path:
- Open an issue to report a bug or propose a feature.
- Fork the repository and create a branch from
main. - Submit a pull request with a clear description of your change.
CaeriusNet is licensed under the MIT License.