A .NET data-access framework built for simplicity, high performance and global scale.
Shard a large data set across servers. Give every tenant a database of their own. Split reads from writes. Map results at hand-written ADO.NET speed. Persist Microsoft Orleans grains in real relational tables. ArgentSea is the only .NET framework designed for scaling data out, and it is a pleasure to use even when you never shard.
Most data frameworks assume one database. ArgentSea assumes many, and makes the ordinary case better along the way.
Spread one data set across many databases, or give each cloud tenant a database of their own. Querying a shard set reads like querying one database: route by key, or fan out to every shard concurrently.
Learn more →Decorate model properties with attributes and let the Mapper move values to parameters and from result sets. Reflection runs once per model; after that it is compiled code at hand-written ADO.NET speed.
Learn more →Every connection property is a setting in an inheritable hierarchy. Set a login once for a whole shard set, keep passwords in a secret store, and promote the same layout across environments.
Learn more →Transient failures are retried with a configurable backoff; persistent failures trip a per-endpoint circuit breaker so a dead database cannot stall your whole service. Every retry is logged.
Learn more →Static SQL in stored procedures or deployed statement files, parameters declared once and copied per connection, and shard queries that run in parallel. The framework is never the bottleneck.
Learn more →The only Microsoft Orleans storage provider that keeps grain state in third-normal-form tables instead of blobs, so SQL can query across grains. It clusters silos through SQL and runs many tenant databases behind one cluster.
Learn more →Whether you partition a huge data set into shards or isolate every customer in a tenant database, the topology is the same: a shard set of databases, each with a read and a write endpoint. Your code addresses the set, or a single shard by key, and ArgentSea opens the right connections, copies the parameters, runs the shards concurrently and merges the results.
ShardKey carries the shard id, so the right database is always known.ReadAll, ReadFirst and Write run across every shard, or a targeted subset, in parallel. A model declares how its properties map to parameters and columns. A Query names a stored procedure or a deployed SQL file. One call on a Read or Write connection, or on a whole shard set, does the rest: open, parameterize, execute, retry if needed, map, log.
appsettings.json.AddSqlServices or AddPgServices.public readonly record struct Customer : IKeyedModel<int>
{
[MapSqlShardKey("@CustomerId")]
[MapToSqlInt("@CustomerId")]
public ShardKey<int> Key { get; init; }
[MapToSqlNVarChar("@Name", 100)]
public string Name { get; init; }
[MapToSqlNVarChar("@Email", 255)]
public string? Email { get; init; }
}
public static class Queries
{
public static QueryProcedure GetCustomer
=> new("rdr.GetCustomer", ["CustomerId"]);
}
public class CustomerStore(SqlShardSets shardSets, ILogger<CustomerStore> logger)
{
private readonly SqlShardSets.ShardSet _customers = shardSets["Customers"];
public Task<Customer> GetAsync(ShardKey<int> key, CancellationToken cancellation)
{
var prms = new ParameterCollection()
.AddSqlIntInputParameter("@CustomerId", key.RecordId);
return _customers[key].Read
.MapReaderAsync<Customer>(Queries.GetCustomer, prms, cancellation);
}
}public readonly record struct Customer : IKeyedModel<int>
{
[MapPgShardKey("customer_id")]
[MapToPgInteger("customer_id")]
public ShardKey<int> Key { get; init; }
[MapToPgVarchar("name", 100)]
public string Name { get; init; }
[MapToPgVarchar("email", 255)]
public string? Email { get; init; }
}
public static class Queries
{
// Loads SQL/GetCustomer.psql the first time it is used.
private static readonly Lazy<QueryStatement> _getCustomer
= QueryStatement.Create("GetCustomer", ["customer_id"]);
public static QueryStatement GetCustomer => _getCustomer.Value;
}
public class CustomerStore(PgShardSets shardSets, ILogger<CustomerStore> logger)
{
private readonly PgShardSets.ShardSet _customers = shardSets["Customers"];
public Task<Customer> GetAsync(ShardKey<int> key, CancellationToken cancellation)
{
var prms = new ParameterCollection()
.AddPgIntegerInputParameter("customer_id", key.RecordId);
return _customers[key].Read
.MapReaderAsync<Customer>(Queries.GetCustomer, prms, cancellation);
}
}Built on .NET 10 with Microsoft.Data.SqlClient and Npgsql. Version 1 of ArgentSea ran in production for years and handled billions of dollars in transactions; version 2 is the current release. Open source under the MIT license.