DataOil.Client
0.2.2
Prefix Reserved
dotnet add package DataOil.Client --version 0.2.2
NuGet\Install-Package DataOil.Client -Version 0.2.2
<PackageReference Include="DataOil.Client" Version="0.2.2" />
<PackageVersion Include="DataOil.Client" Version="0.2.2" />
<PackageReference Include="DataOil.Client" />
paket add DataOil.Client --version 0.2.2
#r "nuget: DataOil.Client, 0.2.2"
#:package DataOil.Client@0.2.2
#addin nuget:?package=DataOil.Client&version=0.2.2
#tool nuget:?package=DataOil.Client&version=0.2.2
DataOil.Client
The Data Oil client for .NET. One statement over rows, vectors, keywords and relationships,
streamed as NDJSON, with the two tenancy headers, typed errors and the retry a lost race needs
built in. net8.0 and net10.0; no dependencies beyond the framework.
dotnet add package DataOil.Client
Sixty seconds
using DataOil.Client;
using DataOilClient client = new("https://acme.dataoil.tech", "bookshop", "your-account", "your-password");
// or, with an API key: new("https://acme.dataoil.tech", "bookshop", "dot_...")
await client.StatementAsync(
"INSERT INTO Book SET isbn = :isbn, title = :title",
new { isbn = "978-cs", title = "From C#" });
foreach (JsonElement row in await client.StatementAsync("SELECT isbn, title FROM Book ORDER BY isbn"))
{
Console.WriteLine($"{row.GetProperty("isbn")} {row.GetProperty("title")}");
}
// A large scan streams, and follows the cursor across pages.
await foreach (JsonElement row in client.QueryAsync("SELECT FROM Report WHERE filedAt > :since", new { since = "2026-01-01" }, pageSize: 2000))
{
Handle(row);
}
// One statement over keywords, meaning and a column.
var hits = await client.StatementAsync("""
SELECT title, VECTOR_DISTANCE(embedding, EMBED(:q), 'cosine') AS distance, $score AS keywordScore
FROM Report
WHERE SEARCH_INDEX('Report[body]', :q) = true AND filedAt > date('2026-01-01')
ORDER BY distance LIMIT 10
""", new { q = "casing failure" });
What it does for you
| The two headers | X-Job-ID and X-Database on every call. The engine refuses a call without either, one at a time. WithJob("nightly-42") names a batch. |
| NDJSON, streamed | QueryAsync yields rows as they arrive and sends the cursor back for the next page. The trailer line is never a row. StatementAsync reads one page into a list and refuses to pretend a page that did not finish was the whole answer. |
| Typed errors | Every refusal is a DataOilException with Status, ErrorCode (the stable code, e.g. TypeNotFound, RequestQuotaExceeded), Detail, CorrelationId, Arguments (a quota's used, allowed, resetsAt) and Retryable. Four subclasses: DataOilAuthorizationException (401/403), DataOilNotFoundException (404), DataOilConflictException (409/412), DataOilQuotaException (429/507, with RetryAfter). |
| The 409 retry | A lost race is answered 409 with X-Retryable: true; nothing was written, and the same request a moment later succeeds. It is retried with full jitter on its own budget (RetryPolicy: 5 attempts, 100 ms doubling to 5 s). A duplicate key (X-Retryable: false) is never retried. 429 waits what Retry-After says when that is inside the budget; 503 is retried like a race. Nothing else is. |
The rest of the surface
JsonElement record = await client.CreateAsync("Book", new { isbn = "1", title = "A" }); // {rid, type, properties}
JsonElement read = await client.GetAsync(record.GetProperty("rid").GetString()!);
await client.DeleteAsync(record.GetProperty("rid").GetString()!);
await client.BatchAsync([new("INSERT INTO Book SET isbn = :i", new { i = "1" }), new("INSERT INTO Book SET isbn = :i", new { i = "2" })]);
string tx = await client.BeginTransactionAsync();
await client.StatementAsync("INSERT INTO Book SET isbn = '3'", transaction: tx);
await client.CommitAsync(tx); // or RollbackAsync
JsonElement schema = await client.SchemaAsync(); // {types:[{name, properties:[{name,type,description}], description}]}
JsonElement prose = await client.SchemaNarrativeAsync();
JsonElement key = await client.MintTokenAsync("the reporting job", rights: ["read"], lifetime: TimeSpan.FromDays(30));
JsonElement answer = await client.AskAsync("which reports mention casing?", rows, statement: sql); // the tenant's own model, sent only the rows you read
JsonElement anything = await client.RequestAsync(HttpMethod.Get, "api/v2/databases/bookshop/statistics");
DataOilClientOptions takes an HttpClient you own (for a deployment on its own certificate
authority, a proxy, or a test) and a RetryPolicy.
Keeping a schema (0.2.0)
DataOilSchemaAnswer first = await client.GetSchemaAsync(); // what this caller may read, typed
DataOilSchema schema = first.Schema!; // Version, Types, Properties, Indexes
DataOilSchemaAnswer again = await client.GetSchemaAsync(ifNoneMatch: first.Tag);
// again.NotModified is true when nothing this caller may read has changed: one small request
int version = await client.SchemaVersionAsync(); // the number alone
DataOilCapabilities can = await client.CapabilitiesAsync(); // functions, types, joins, transactions, paging
client.ExpectedSchemaVersion = schema.Version; // sent as X-Schema-Version
// a statement checked against a version the schema has moved past is refused before it runs:
// DataOilException.IsSchemaMoved
The tag is a hash of the answer, so it moves when a grant changes what this caller may read,
which moves no schema version. SchemaVersionAsync says whether the schema moved; only the tag
says whether your view of it did. These are what the Entity Framework Core provider,
DataOil.EntityFrameworkCore, is built on.
Tests
dotnet test runs 27 checks against a stub that answers exactly what the engine answers - NDJSON
with its trailer, problem documents with X-Retryable, a 429 with Retry-After. Set DATAOIL_URL,
DATAOIL_DATABASE and a credential (and DATAOIL_CA for a self-signed deployment) and one more
creates a type, writes, streams, refuses and drops against a running engine.
The reference manual is at https://documents.dataoil.tech/tenant/.
Source and licensing
Questions, issues and releases: https://documents.dataoil.tech/tenant/ and info@dataoil.tech. The packages are published to the public registries as compiled packages; their source is not published.
This library is proprietary and licensed only for legitimate use with Data Oil, under the Data Oil Software
Licence (see LICENSE): copying, modifying, redistributing or reverse engineering it, and any attack on
or unauthorised access to the platform, are prohibited. Data Oil itself is a commercial service of MaxDragon
Technology, and using it needs a credential issued under the Data Oil customer agreement. The
library grants no right to the service, and the service applies its own rights, budgets and audit
to every credential. The NOTICE file in the package has the full statement, the trademark note
and the third-party components.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. 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
- No dependencies.
-
net8.0
- No dependencies.
NuGet packages (1)
Showing the top 1 NuGet packages that depend on DataOil.Client:
| Package | Downloads |
|---|---|
|
DataOil.EntityFrameworkCore
The Entity Framework Core provider for Data Oil. It learns each database's schema at run time over the engine's own API, validates the application's model against it, translates LINQ to the engine's dialect, and saves with the record version as the concurrency token. |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 0.2.2 | 117 | 10/24/2026 |