OxiDB now has first-class .NET support with three NuGet packages: a pure managed TCP client, an embedded FFI client that runs the database in-process, and a full Entity Framework Core provider that supports both modes. All three target .NET 10.0 and are available for download from the downloads page.
Three Packages, One Interface
The key design principle is a shared IOxiDbClient interface. Both the TCP and embedded clients implement the same interface, which means you can swap between network and embedded mode without changing your application code:
| Package | Mode | Size | Use Case |
|---|---|---|---|
OxiDb.Client.Tcp | Network client | 16 KB | Connect to a remote OxiDB server |
OxiDb.Client.Embedded | In-process FFI | 2.3 MB | Embed the database directly in your app |
OxiDb.EntityFrameworkCore | EF Core provider | 23 KB | Use OxiDB with LINQ, change tracking, migrations |
OxiDb.Client.Tcp — Pure Managed TCP Client
A lightweight, zero-dependency TCP client that speaks OxiDB's length-prefixed protocol. Thread-safe via SemaphoreSlim, fully async, and supports both JSON and OxiWire binary encoding.
Connect
using OxiDb.Client.Tcp;
// Basic connection
var client = await OxiDbTcpClient.ConnectAsync("127.0.0.1", 4444);
// With SCRAM-SHA-256 authentication
var client = await OxiDbTcpClient.ConnectAsync("127.0.0.1", 4444, "admin", "secret");
// Enable OxiWire binary protocol for faster encoding
client.UseOxiWire();
CRUD Operations
// Insert
await client.InsertAsync("products", new { name = "Widget", price = 19.99, category = "tools" });
// Batch insert
await client.InsertManyAsync("products", new[] {
new { name = "Gadget", price = 29.99, category = "electronics" },
new { name = "Gizmo", price = 9.99, category = "tools" }
});
// Find with query operators
var expensive = await client.FindAsync("products", new { price = new { $gt = 15 } });
// Find one
var widget = await client.FindOneAsync("products", new { name = "Widget" });
// Update
await client.UpdateAsync("products",
new { category = "tools" }, // filter
new { $set = new { category = "hand-tools" } } // update
);
// Delete
await client.DeleteAsync("products", new { price = new { $lt = 10 } });
// Count
var count = await client.CountAsync("products", new { category = "electronics" });
Indexes
await client.CreateIndexAsync("products", "category");
await client.CreateUniqueIndexAsync("products", "sku");
await client.CreateCompositeIndexAsync("products", new[] { "category", "price" });
var indexes = await client.ListIndexesAsync("products");
Aggregation
var pipeline = new object[] {
new { $match = new { category = "electronics" } },
new { $group = new { _id = "$brand", total = new { $sum = "$price" } } },
new { $sort = new { total = -1 } },
new { $limit = 10 }
};
var results = await client.AggregateAsync("products", pipeline);
Transactions
// Manual transaction
await client.BeginTransactionAsync();
try {
await client.InsertAsync("orders", new { item = "Widget", qty = 5 });
await client.UpdateAsync("inventory", new { item = "Widget" }, new { $inc = new { qty = -5 } });
await client.CommitTransactionAsync();
} catch {
await client.RollbackTransactionAsync();
throw;
}
// Or use the convenience wrapper
await client.WithTransactionAsync(async () => {
await client.InsertAsync("orders", new { item = "Widget", qty = 5 });
await client.UpdateAsync("inventory", new { item = "Widget" }, new { $inc = new { qty = -5 } });
});
OxiDb.Client.Embedded — In-Process Database
Run OxiDB directly inside your .NET process with zero network overhead. The embedded client wraps the native Rust library via P/Invoke (C FFI), giving you the full power of the OxiDB engine without a separate server process.
using OxiDb.Client.Embedded;
// Open a database at a file path
using var db = OxiDbEmbeddedClient.Open("./oxidb_data");
// Or open with AES-GCM transparent encryption
using var db = OxiDbEmbeddedClient.OpenEncrypted("./oxidb_data", "./encryption.key");
// Same API as TCP client — IOxiDbClient interface
await db.InsertAsync("users", new { name = "Alice", email = "[email protected]" });
var users = await db.FindAsync("users", new { name = "Alice" });
The embedded package includes native runtimes for:
- macOS —
liboxidb_embedded_ffi.dylib(ARM64 + x86_64) - Linux —
liboxidb_embedded_ffi.so(x86_64, musl static) - Windows —
oxidb_embedded_ffi.dll(x86_64)
The native library is automatically selected at runtime based on your platform. No manual configuration needed.
When to Use Embedded vs TCP
| Embedded | TCP | |
|---|---|---|
| Latency | Microseconds (in-process) | Milliseconds (network) |
| Deployment | Single process, no server | Separate server process |
| Concurrency | Single process only | Multiple clients |
| Use case | Desktop apps, CLI tools, tests | Web apps, microservices |
| Authentication | Not needed (in-process) | SCRAM-SHA-256 |
| Encryption | AES-GCM at storage layer | TLS + AES-GCM |
OxiDb.EntityFrameworkCore — EF Core Provider
The crown jewel: a full Entity Framework Core provider that lets you use OxiDB with LINQ queries, change tracking, dependency injection, and all the EF Core patterns .NET developers already know.
Setup
using OxiDb.EntityFrameworkCore;
// TCP mode
services.AddDbContext<AppDbContext>(options =>
options.UseOxiDb("127.0.0.1", 4444)
);
// With authentication
services.AddDbContext<AppDbContext>(options =>
options.UseOxiDb("127.0.0.1", 4444, username: "admin", password: "secret")
);
// Embedded mode
services.AddDbContext<AppDbContext>(options =>
options.UseOxiDbEmbedded("./oxidb_data")
);
// Embedded with encryption
services.AddDbContext<AppDbContext>(options =>
options.UseOxiDbEmbedded("./oxidb_data", encryptionKeyPath: "./encryption.key")
);
Define Your Model
public class AppDbContext : DbContext
{
public DbSet<Product> Products => Set<Product>();
public DbSet<Order> Orders => Set<Order>();
public AppDbContext(DbContextOptions<AppDbContext> options) : base(options) { }
}
public class Product
{
public string Id { get; set; }
public string Name { get; set; }
public decimal Price { get; set; }
public string Category { get; set; }
}
public class Order
{
public string Id { get; set; }
public string ProductId { get; set; }
public int Quantity { get; set; }
public DateTime CreatedAt { get; set; }
}
LINQ Queries
// All products in a category
var electronics = await db.Products
.Where(p => p.Category == "Electronics")
.OrderBy(p => p.Price)
.ToListAsync();
// Find by ID
var product = await db.Products.FindAsync("abc123");
// Count with filter
var count = await db.Products.CountAsync(p => p.Price > 100);
// First or default
var cheapest = await db.Products
.OrderBy(p => p.Price)
.FirstOrDefaultAsync();
// Pagination
var page = await db.Products
.Skip(20)
.Take(10)
.ToListAsync();
Change Tracking
// Insert
db.Products.Add(new Product { Name = "Widget", Price = 19.99m, Category = "Tools" });
await db.SaveChangesAsync();
// Update — only modified fields are sent
var product = await db.Products.FindAsync("abc123");
product.Price = 24.99m;
await db.SaveChangesAsync(); // sends: $set { price: 24.99 }
// Delete
db.Products.Remove(product);
await db.SaveChangesAsync();
Transactions
using var transaction = await db.Database.BeginTransactionAsync();
try
{
db.Orders.Add(new Order { ProductId = "abc123", Quantity = 5 });
var product = await db.Products.FindAsync("abc123");
product.Stock -= 5;
await db.SaveChangesAsync();
await transaction.CommitAsync();
}
catch
{
await transaction.RollbackAsync();
throw;
}
ASP.NET Minimal API Example
A complete REST API in under 30 lines:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<ShopContext>(options =>
options.UseOxiDb(
builder.Configuration["OxiDb:Host"] ?? "127.0.0.1",
int.Parse(builder.Configuration["OxiDb:Port"] ?? "4444")
)
);
var app = builder.Build();
app.MapGet("/products", async (ShopContext db) =>
await db.Products.ToListAsync()
);
app.MapGet("/products/{id}", async (ShopContext db, string id) =>
await db.Products.FindAsync(id) is Product p ? Results.Ok(p) : Results.NotFound()
);
app.MapPost("/products", async (ShopContext db, Product product) =>
{
db.Products.Add(product);
await db.SaveChangesAsync();
return Results.Created($"/products/{product.Id}", product);
});
app.MapDelete("/products/{id}", async (ShopContext db, string id) =>
{
if (await db.Products.FindAsync(id) is Product p)
{
db.Products.Remove(p);
await db.SaveChangesAsync();
return Results.NoContent();
}
return Results.NotFound();
});
app.Run();
Property Mapping
The EF Core provider automatically maps C# PascalCase property names to OxiDB's snake_case field names:
| C# Property | OxiDB Field |
|---|---|
FirstName | first_name |
CreatedAt | created_at |
ProductId | product_id |
Id | _key (primary key) |
Entity types map to collections by their short name in lowercase. DbSet<Product> maps to collection "product", DbSet<Order> maps to "order".
Architecture
┌─────────────────────────────────────────────┐
│ Your Application │
│ │
│ DbContext ←→ LINQ ←→ Change Tracker │
└──────────────────┬──────────────────────────┘
│
OxiDb.EntityFrameworkCore
(query translation, DI, mapping)
│
┌────────┴────────┐
│ IOxiDbClient │
└────────┬────────┘
┌───────┴───────┐
│ │
OxiDb.Client.Tcp OxiDb.Client.Embedded
(network client) (in-process FFI)
│ │
┌──────┴──────┐ ┌─────┴─────┐
│ OxiDB Server│ │Native Rust│
│ (TCP) │ │ Library │
└─────────────┘ └───────────┘
Download
OxiDb.Client.Tcp (16 KB) OxiDb.Client.Embedded (2.3 MB) OxiDb.EntityFrameworkCore (23 KB)
# Install via dotnet CLI
dotnet add package OxiDb.Client.Tcp --version 0.18.1
dotnet add package OxiDb.Client.Embedded --version 0.18.1
dotnet add package OxiDb.EntityFrameworkCore --version 0.18.1
All packages target .NET 10.0. The TCP client has zero dependencies. The embedded client includes native runtimes for macOS, Linux, and Windows. The EF Core provider depends on Microsoft.EntityFrameworkCore 10.0.
Discussion 0
No comments yet. Start the conversation.