Palm.SurrealDB.Net.Abstractions
0.2.1
dotnet add package Palm.SurrealDB.Net.Abstractions --version 0.2.1
NuGet\Install-Package Palm.SurrealDB.Net.Abstractions -Version 0.2.1
<PackageReference Include="Palm.SurrealDB.Net.Abstractions" Version="0.2.1" />
<PackageVersion Include="Palm.SurrealDB.Net.Abstractions" Version="0.2.1" />
<PackageReference Include="Palm.SurrealDB.Net.Abstractions" />
paket add Palm.SurrealDB.Net.Abstractions --version 0.2.1
#r "nuget: Palm.SurrealDB.Net.Abstractions, 0.2.1"
#:package Palm.SurrealDB.Net.Abstractions@0.2.1
#addin nuget:?package=Palm.SurrealDB.Net.Abstractions&version=0.2.1
#tool nuget:?package=Palm.SurrealDB.Net.Abstractions&version=0.2.1
Palm.SurrealDB.Net.Abstractions
Core value types and contracts for the Palm.SurrealDB .NET ecosystem for
SurrealDB 3.x. This is the leaf package: it depends on nothing else in
the ecosystem, and every other Palm.SurrealDB.* project depends on it. It defines the vocabulary
everything else speaks — RecordId, Table, SurrealValue, connection options, credential shapes,
the client and type-mapper interfaces, entity-mapping attributes, live-query change types, and the
exception hierarchy — with no wire protocol, no serialization codec, and no network code of its own.
Why this project exists
Every other project in the ecosystem — the CBOR serializer, the WebSocket/HTTP transport, the SurrealQL AST and LINQ provider, the client implementation — needs to agree on what a record id is, what a SurrealDB value looks like in memory, and what shape a connection's options and credentials take. Rather than let each project define its own version of these types (and drift), they live here once, as plain value types and interfaces with no external dependencies. This also makes the project safe to reference from tooling, tests, or a future source-generator without pulling in networking or serialization code.
Installation
dotnet add package Palm.SurrealDB.Net.Abstractions
This project is packable and ships as the Palm.SurrealDB.Net.Abstractions NuGet package. An earlier
version of this section said the opposite, citing a true observation — the .csproj sets neither
PackageId nor IsPackable — in support of a false conclusion: packability comes from the SDK
default and Directory.Build.props, and the only projects that opt
out are the three that set IsPackable=false. PackableSetGuardTests pins all 25 packable ids,
and this is one of them.
Targets net10.0 only. The package is proprietary and sets PackageRequireLicenseAcceptance,
so NuGet prompts before installing — see LICENSE.
Nothing is on NuGet.org until the first release runs (see
docs/RELEASING.md); until then, reference the project directly:
<ItemGroup>
<ProjectReference Include="path/to/src/Palm.SurrealDB.Net.Abstractions/Palm.SurrealDB.Net.Abstractions.csproj" />
</ItemGroup>
Related projects
| Project | Purpose |
|---|---|
Palm.SurrealDB.Net.Serialization |
CBOR codec for SurrealValue and the reflection-based ISurrealTypeMapper. |
Palm.SurrealDB.Net.Protocol |
WebSocket/HTTP RPC transports implementing ISurrealConnection. |
Palm.SurrealDB.Net.SurrealQL |
The SurrealQL AST, emitter, parser, and formatter. |
Palm.SurrealDB.Net |
SurrealDbClient, the ISurrealClient implementation, and DI registration. |
Palm.SurrealDB.Net.Linq |
The IQueryable<T>-to-SurrealQL LINQ provider. |
| Root README | Ecosystem overview, quick start, and build/test commands. |
API reference
Every public type below is drawn directly from source and from
PublicAPI.Shipped.txt / PublicAPI.Unshipped.txt,
the analyzer-enforced baseline of this project's public surface. All types live in the
Palm.SurrealDB.Net namespace.
Identifiers
Table
public readonly record struct Table
{
public string Name { get; }
public Table(string name);
public override string ToString();
public static implicit operator Table(string name);
public RecordId Id(RecordIdKey key);
}
Identifies a SurrealDB table by its raw, unescaped name. The constructor throws
ArgumentNullException for a null name and ArgumentException for empty or whitespace-only names.
default(Table) is invalid (its Name is null) — always construct via the constructor or the
implicit string conversion. Id(RecordIdKey) is the idiomatic way to build a RecordId in this
table: new Table("person").Id(1).
RecordId
public readonly struct RecordId : IEquatable<RecordId>
{
public RecordId(Table table, RecordIdKey key);
public Table Table { get; }
public RecordIdKey Key { get; }
public bool Equals(RecordId other);
public static bool operator ==(RecordId left, RecordId right);
public static bool operator !=(RecordId left, RecordId right);
public static RecordId Parse(string s);
public static bool TryParse(string? s, out RecordId result);
public override string ToString();
}
A SurrealDB record identifier: a table plus a key. default(RecordId) is invalid; always construct
via the constructor.
Parse/TryParseaccept canonicaltable:keytext. Parts are bare identifiers ([A-Za-z0-9_]+),⟨…⟩-escaped, or backtick-escaped. A bare key of only digits (with an optional leading-) parses as an integer key; escaped keys always parse as string keys. Array and object keys have no text form and are rejected byTryParse. A whitespace-only escaped table part (⟨ ⟩:x) is rejected — a table name cannot be whitespace — while a whitespace-only escaped key (x:⟨ ⟩) is valid, becauseRecordIdKeyrejects only null/empty.ParsethrowsFormatExceptionon invalid input (viaArgumentNullExceptionfirst ifsisnull).ToStringformats the canonicaltable:keyform. A UUID key parses back viaTryParseas aRecordIdKeyKind.Stringkey, notRecordIdKeyKind.Uuid— canonical text has no UUID-vs-string distinction; the CBOR wire form is the round-trip-preserving representation. Array and object keys produce a diagnostic-only form (table:[N items]), since their real SurrealQL literal form belongs to the AST layer, not this text format.ToStringthrowsInvalidOperationExceptionifKeyholds an unrecognizedRecordIdKeyKind(a defensive guard against future enum growth, not a normal failure mode).
RecordIdKey / RecordIdKeyKind
public enum RecordIdKeyKind
{
String = 0,
Int64,
Uuid,
Array,
Object,
}
public readonly struct RecordIdKey : IEquatable<RecordIdKey>
{
public RecordIdKeyKind Kind { get; }
public static RecordIdKey From(string key);
public static RecordIdKey From(long key);
public static RecordIdKey From(Guid key);
public static RecordIdKey FromArray(IEnumerable<SurrealValue> items);
public static RecordIdKey FromObject(IEnumerable<KeyValuePair<string, SurrealValue>> fields);
public string AsString();
public long AsInt64();
public Guid AsUuid();
public ImmutableArray<SurrealValue> AsArray();
public ImmutableDictionary<string, SurrealValue> AsObject();
public bool Equals(RecordIdKey other);
public static bool operator ==(RecordIdKey left, RecordIdKey right);
public static bool operator !=(RecordIdKey left, RecordIdKey right);
public static implicit operator RecordIdKey(string key);
public static implicit operator RecordIdKey(long key);
public static implicit operator RecordIdKey(Guid key);
}
The key portion of a RecordId — a tagged union over string, long, Guid, an
ImmutableArray<SurrealValue>, or an ImmutableDictionary<string, SurrealValue>. default(RecordIdKey)
is invalid; always construct via the From* factories or an implicit conversion.
From(string)throwsArgumentExceptionfor an empty (but notnull-checked separately —ArgumentException.ThrowIfNullOrEmptycovers both) string.- Each
As*accessor throwsInvalidOperationException("The key holds a {Kind}, not a {requested}.") when called against the wrongKind. - Implicit conversions exist for
string,long, andGuid; array and object keys must go throughFromArray/FromObjectexplicitly.
Values
SurrealValue / SurrealValueKind
public readonly struct SurrealValue : IEquatable<SurrealValue>
{
public SurrealValueKind Kind { get; }
public static SurrealValue None { get; } // == default(SurrealValue)
public static SurrealValue Null { get; }
public static SurrealValue From(bool value);
public static SurrealValue From(long value);
public static SurrealValue From(double value);
public static SurrealValue From(decimal value);
public static SurrealValue From(string value);
public static SurrealValue From(DateTimeOffset value);
public static SurrealValue From(SurrealDuration value);
public static SurrealValue From(Guid value);
public static SurrealValue From(ReadOnlyMemory<byte> value);
public static SurrealValue From(Table value);
public static SurrealValue From(Geometry value);
public static SurrealValue From(SurrealRange value);
public static SurrealValue From(RecordId value);
public static SurrealValue FromArray(IEnumerable<SurrealValue> items);
public static SurrealValue FromObject(IEnumerable<KeyValuePair<string, SurrealValue>> fields);
public bool GetBoolean();
public long GetInt64();
public double GetDouble();
public decimal GetDecimal();
public string GetString();
public DateTimeOffset GetDateTime();
public SurrealDuration GetDuration();
public Guid GetUuid();
public ReadOnlyMemory<byte> GetBytes();
public Table GetTable();
public Geometry GetGeometry();
public SurrealRange GetRange();
public RecordId GetRecordId();
public ImmutableArray<SurrealValue> GetArray();
public ImmutableDictionary<string, SurrealValue> GetObject();
public bool Equals(SurrealValue other);
public static bool operator ==(SurrealValue left, SurrealValue right);
public static bool operator !=(SurrealValue left, SurrealValue right);
// Implicit conversions: bool, long, double, decimal, string, DateTimeOffset,
// Guid, SurrealDuration, Table, RecordId.
public override string ToString();
}
An immutable tagged union over every SurrealDB value kind — the document model used for schemaless
data and as the materialization intermediate (comparable in role to System.Text.Json.JsonElement).
SurrealValueKind enumerates the sixteen kinds: None = 0, Null = 1, Boolean, Integer,
Float, Decimal, String, Duration, DateTime, Uuid, Bytes, RecordId, Table, Array,
Object, Range, Geometry.
None vs Null — this distinction is load-bearing, not cosmetic. SurrealValue.None
(SurrealValueKind.None, value 0) is also default(SurrealValue) and represents an absent
field — SurrealDB's NONE. SurrealValue.Null represents an explicit null value — SurrealDB's
NULL. The server treats these as genuinely different, non-interchangeable comparisons: a record
created with a field set to NONE omits that key from SELECT * FROM ... entirely, WHERE field = NULL does not match an absent field, and only WHERE field = NONE (or IS NONE) does. This was
live-verified against a running SurrealDB 3.1.4 server and is documented as a real incident in the
repository's LESSONS-LEARNED.md (2026-07-11): the reflection-based type mapper has always mapped
CLR null to SurrealValue.None on the storage path (a null-valued member serializes as an absent
field), and the LINQ layer was fixed to route every literal — null included — through that same
mapper, so a Where(p => p.Email == null) predicate and a stored null member now agree on
SurrealValue.None by construction. Any new code that needs to compare against SurrealDB's NULL
must use SurrealValue.Null explicitly; CLR null means NONE.
Each Get* accessor throws InvalidOperationException when Kind doesn't match (message:
"The value holds a {Kind}, not a {requested}."). Geometry and SurrealRange intentionally have
no implicit conversion — both are reference types where an implicit conversion would invite
accidental-boxing-style confusion at call sites — use From(...) explicitly. Byte buffers are
likewise explicit-only (From(ReadOnlyMemory<byte>)) to avoid ambiguity against other implicit
numeric/array conversions; the input is copied. ToString() returns a short diagnostic description
(e.g. "NONE", "true", "[3 items]", "{2 fields}", "<16 bytes>"), not a SurrealQL literal —
literal emission belongs to the AST layer (Palm.SurrealDB.Net.SurrealQL).
SurrealDuration
public readonly record struct SurrealDuration : ISpanParsable<SurrealDuration>, IComparable<SurrealDuration>
{
public static SurrealDuration Zero { get; }
public ulong Seconds { get; }
public uint Nanoseconds { get; }
public SurrealDuration(ulong seconds, uint nanoseconds);
public static SurrealDuration Parse(string s);
public static SurrealDuration Parse(string s, IFormatProvider? provider);
public static SurrealDuration Parse(ReadOnlySpan<char> s, IFormatProvider? provider);
public static bool TryParse(string? s, IFormatProvider? provider, out SurrealDuration result);
public static bool TryParse(ReadOnlySpan<char> s, IFormatProvider? provider, out SurrealDuration result);
public static SurrealDuration FromTimeSpan(TimeSpan value);
public TimeSpan ToTimeSpan();
public int CompareTo(SurrealDuration other);
public override string ToString();
public static bool operator <(SurrealDuration left, SurrealDuration right);
public static bool operator >(SurrealDuration left, SurrealDuration right);
public static bool operator <=(SurrealDuration left, SurrealDuration right);
public static bool operator >=(SurrealDuration left, SurrealDuration right);
}
A non-negative SurrealDB duration with nanosecond precision. SurrealDB durations exceed TimeSpan in
both range and precision: whole seconds are a 64-bit unsigned count and the fractional part is
nanoseconds (a TimeSpan tick is 100 ns). The text form concatenates <number><unit> tokens using
units y (365 days), w, d, h, m, s, ms, us/µs, and ns, e.g. "1h30m".
SurrealDuration(ulong, uint)throwsArgumentOutOfRangeExceptionwhennanoseconds >= 1_000_000_000.ParsethrowsFormatExceptionfor empty input, an unrecognized unit, or a missing number, andOverflowExceptionif the accumulated value overflows (arithmetic ischecked).FromTimeSpanthrowsArgumentOutOfRangeExceptionfor a negativeTimeSpan.ToTimeSpanthrowsOverflowExceptionif the duration exceedsTimeSpan.MaxValue, truncating sub-tick nanoseconds otherwise.ToStringformats canonical unit form (e.g."1m30s"), or"0ns"for the zero duration.
SurrealRange / RangeBound
public readonly record struct RangeBound(SurrealValue Value, bool IsInclusive);
public sealed record SurrealRange(RangeBound? Start, RangeBound? End);
A SurrealDB range value. Either bound may be null (unbounded); each present bound carries its own
IsInclusive flag, so 1..=10, 1<..10, ..10, and similar SurrealQL range forms are all
representable.
Geometry (Geometry, GeoPoint, and the seven concrete shapes)
public readonly record struct GeoPoint(double Longitude, double Latitude);
public abstract class Geometry : IEquatable<Geometry>
{
public abstract bool Equals(Geometry? other);
public abstract override int GetHashCode();
}
public sealed class GeometryPoint : Geometry
{
public GeometryPoint(GeoPoint coordinate);
public GeoPoint Coordinate { get; }
}
public sealed class GeometryLine : Geometry
{
public GeometryLine(IEnumerable<GeoPoint> points); // >= 2 points required
public ImmutableArray<GeoPoint> Points { get; }
public bool IsClosed { get; } // first point == last point
}
public sealed class GeometryPolygon : Geometry
{
public GeometryPolygon(IEnumerable<GeometryLine> rings); // >= 1 ring; each closed, >= 4 points
public ImmutableArray<GeometryLine> Rings { get; } // Rings[0] is the exterior ring
}
public sealed class GeometryMultiPoint : Geometry
{
public GeometryMultiPoint(IEnumerable<GeoPoint> points); // may be empty
public ImmutableArray<GeoPoint> Points { get; }
}
public sealed class GeometryMultiLine : Geometry
{
public GeometryMultiLine(IEnumerable<GeometryLine> lines); // may be empty
public ImmutableArray<GeometryLine> Lines { get; }
}
public sealed class GeometryMultiPolygon : Geometry
{
public GeometryMultiPolygon(IEnumerable<GeometryPolygon> polygons); // may be empty
public ImmutableArray<GeometryPolygon> Polygons { get; }
}
public sealed class GeometryCollection : Geometry
{
public GeometryCollection(IEnumerable<Geometry> geometries); // may be empty
public ImmutableArray<Geometry> Geometries { get; }
}
GeoPoint is (Longitude, Latitude) — GeoJSON ordering (x, y), not (lat, lng). Geometry is the
abstract base with GeoJSON semantics; all seven concrete shapes are immutable, value-equatable, and
constructed by copying their input sequence into an ImmutableArray.
GeometryLinethrowsArgumentExceptionwhen fewer than two points are supplied.GeometryPolygonthrowsArgumentExceptionwhen no rings are supplied, or when any ring is open (first point != last point) or has fewer than four points.GeometryMultiPoint,GeometryMultiLine,GeometryMultiPolygon, andGeometryCollectionall accept empty input.- All constructors throw
ArgumentNullExceptionfor anullsequence.
Connection & Options
SurrealDbOptions / ReconnectOptions
public sealed record ReconnectOptions
{
public bool Enabled { get; init; } = true;
public TimeSpan InitialDelay { get; init; } = TimeSpan.FromMilliseconds(200);
public TimeSpan MaxDelay { get; init; } = TimeSpan.FromSeconds(30);
public int? MaxAttempts { get; init; }
public double JitterFactor { get; init; } = 0.25;
}
public sealed record SurrealDbOptions
{
public required Uri Endpoint { get; init; }
public string? Namespace { get; init; }
public string? Database { get; init; }
public SurrealCredentials? Credentials { get; init; }
public TimeSpan ConnectTimeout { get; init; } = TimeSpan.FromSeconds(5);
public TimeSpan RequestTimeout { get; init; } = TimeSpan.FromSeconds(30);
public ReconnectOptions Reconnect { get; init; } = new();
public int LiveQueryChannelCapacity { get; init; } = 1024;
public void Validate();
}
ReconnectOptions is the exponential-backoff-with-jitter policy for the WebSocket transport.
SurrealDbOptions is the top-level connection configuration passed to a client.
Validate() throws SurrealDbConfigurationException when:
Endpoint.Schemeis not one ofws,wss,http,https.Databaseis set butNamespaceis not (a database requires a namespace).ConnectTimeoutorRequestTimeoutis not positive.LiveQueryChannelCapacity < 1.Reconnect.JitterFactoris outside[0, 1].Reconnect.InitialDelayis not positive, orReconnect.MaxDelay < Reconnect.InitialDelay.Reconnect.MaxAttemptsis set and< 1.
ConnectionState
public enum ConnectionState
{
Disconnected = 0,
Connecting = 1,
Connected = 2,
Reconnecting = 3,
}
The lifecycle state exposed by ISurrealConnection.State.
Credentials
public abstract record SurrealCredentials;
public sealed record RootCredentials(string Username, string Password) : SurrealCredentials;
public sealed record NamespaceCredentials(string Namespace, string Username, string Password) : SurrealCredentials;
public sealed record DatabaseCredentials(string Namespace, string Database, string Username, string Password) : SurrealCredentials;
public sealed record AccessCredentials(
string Namespace,
string Database,
string AccessMethod,
IReadOnlyDictionary<string, SurrealValue>? Parameters = null) : SurrealCredentials;
public sealed record TokenCredentials(string Token) : SurrealCredentials;
SurrealCredentials is the closed base type for every credential shape accepted by SurrealDB
sign-in; its constructor is private protected, so only the five sealed records in this file can
derive from it. Each subtype maps to one SurrealDB authentication level:
RootCredentials— root user/password.NamespaceCredentials— namespace-scoped user/password.DatabaseCredentials— database-scoped user/password.AccessCredentials— record-access (DEFINE ACCESS) sign-in, with optional extra sign-in variables viaParameters. Passed toISurrealClient.SignUpAsync, notSignInAsync.TokenCredentials— an existing JWT, used withISurrealClient.AuthenticateAsync-style flows.
Client Contracts
ISurrealClient
public interface ISurrealClient : IAsyncDisposable
{
ISurrealTypeMapper TypeMapper { get; }
Task ConnectAsync(CancellationToken cancellationToken = default);
Task UseAsync(string @namespace, string? database = null, CancellationToken cancellationToken = default);
Task<string> SignInAsync(SurrealCredentials credentials, CancellationToken cancellationToken = default);
Task<string> SignUpAsync(AccessCredentials credentials, CancellationToken cancellationToken = default);
Task AuthenticateAsync(string token, CancellationToken cancellationToken = default);
Task InvalidateAsync(CancellationToken cancellationToken = default);
Task LetAsync(string name, SurrealValue value, CancellationToken cancellationToken = default);
Task UnsetAsync(string name, CancellationToken cancellationToken = default);
Task<T> CreateAsync<T>(Table table, T content, CancellationToken cancellationToken = default);
Task<T> CreateAsync<T>(RecordId id, T content, CancellationToken cancellationToken = default);
Task<IReadOnlyList<T>> InsertAsync<T>(Table table, IEnumerable<T> contents, CancellationToken cancellationToken = default);
Task<IReadOnlyList<T>> SelectAsync<T>(Table table, CancellationToken cancellationToken = default);
Task<T?> SelectAsync<T>(RecordId id, CancellationToken cancellationToken = default);
Task<T?> UpdateAsync<T>(RecordId id, T content, CancellationToken cancellationToken = default);
Task<T?> UpsertAsync<T>(RecordId id, T content, CancellationToken cancellationToken = default);
Task<T?> MergeAsync<T>(RecordId id, SurrealValue merge, CancellationToken cancellationToken = default);
Task<T?> PatchAsync<T>(RecordId id, IReadOnlyList<SurrealPatchOperation> operations, CancellationToken cancellationToken = default);
Task<T?> DeleteAsync<T>(RecordId id, CancellationToken cancellationToken = default);
Task DeleteAsync(Table table, CancellationToken cancellationToken = default);
Task<SurrealQueryResponse> QueryAsync(
string surql,
IReadOnlyDictionary<string, SurrealValue>? variables = null,
CancellationToken cancellationToken = default);
IAsyncEnumerable<LiveQueryChange<T>> LiveAsync<T>(Table table, CancellationToken cancellationToken = default);
Task<SurrealValue> RunAsync(
string function,
IReadOnlyList<SurrealValue>? arguments = null,
CancellationToken cancellationToken = default);
Task<string> VersionAsync(CancellationToken cancellationToken = default);
}
The full SurrealDB client surface — connection lifecycle, session management, typed CRUD, raw
parameterized queries, live queries, server-side function calls, and version discovery. Implemented
by SurrealDbClient in Palm.SurrealDB.Net. Notable behavior, per the XML
docs on the interface itself:
SignInAsyncthrowsSurrealDbAuthenticationExceptionon failure.SelectAsync<T>(RecordId, ...),UpdateAsync,UpsertAsync,MergeAsync,PatchAsync, andDeleteAsync<T>(RecordId, ...)all returnnullwhen the target record does not exist, rather than throwing.QueryAsync'ssurqlparameter takes$name-style parameters viavariables; never build query text by string interpolation — see thePalm.SurrealDB.Net.SurrealQLAST/emitter for the only sanctioned way to build query text.LiveAsync<T>throwsSurrealDbCapabilityExceptionwhen the active transport does not support live queries; cancelling the token sends akillfor the live query.
ISurrealConnection
public interface ISurrealConnection : IAsyncDisposable
{
ConnectionState State { get; }
bool SupportsLiveQueries { get; }
Task ConnectAsync(CancellationToken cancellationToken = default);
Task<SurrealValue> SendAsync(
string method,
IReadOnlyList<SurrealValue> parameters,
CancellationToken cancellationToken = default);
IAsyncEnumerable<LiveNotification> SubscribeAsync(
Guid liveQueryId,
CancellationToken cancellationToken = default);
}
A lower-level transport abstraction beneath ISurrealClient — one RPC-capable connection. Implemented
by the WebSocket/HTTP transports in Palm.SurrealDB.Net.Protocol.
SendAsync throws SurrealDbRpcException when the server answers with an error object, and
SurrealDbConnectionException when the transport itself fails. SubscribeAsync throws
SurrealDbCapabilityException when SupportsLiveQueries is false.
ISurrealTypeMapper / ISurrealSerializer
public interface ISurrealTypeMapper
{
SurrealNamingPolicy NamingPolicy { get; }
SurrealValue ToSurrealValue<T>(T? value);
T? FromSurrealValue<T>(SurrealValue value);
}
public interface ISurrealSerializer
{
void Serialize(IBufferWriter<byte> writer, SurrealValue value);
SurrealValue Deserialize(ReadOnlySequence<byte> data);
}
ISurrealTypeMapper maps between CLR types and SurrealValue — the single source of truth for
property↔field name mapping (see SurrealNamingPolicy
below). Implementations may be reflection-based (the default) or source-generated for AOT; the
contract is identical either way. The interface methods are deliberately unannotated for
trimming/AOT: source-generated mappers are reflection-free, and the reflection-based default
carries [RequiresDynamicCode]/[RequiresUnreferencedCode] on its own constructor instead
(the IJsonTypeInfoResolver/DefaultJsonTypeInfoResolver shape).
ISurrealSerializer encodes/decodes SurrealValue payloads on the wire (CBOR); implemented by
Palm.SurrealDB.Net.Serialization.
Attributes
SurrealTableAttribute
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = true)]
public sealed class SurrealTableAttribute : Attribute
{
public SurrealTableAttribute(string name);
public string Name { get; }
}
Declares the SurrealDB table name for an entity type. When absent, the active SurrealNamingPolicy
applied to the CLR type name is used instead. The constructor throws ArgumentException for a
null, empty, or whitespace-only name.
SurrealFieldAttribute
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, AllowMultiple = false, Inherited = true)]
public sealed class SurrealFieldAttribute : Attribute
{
public SurrealFieldAttribute();
public SurrealFieldAttribute(string name);
public string? Name { get; }
public bool Ignore { get; init; }
}
Controls how a property or field maps to a SurrealDB object field. With no explicit name, the
active naming policy applies to the CLR member name. Set Ignore = true to exclude a member from
serialization entirely.
SurrealNamingPolicy / SurrealNamingPolicyExtensions
public enum SurrealNamingPolicy
{
AsIs = 0,
CamelCase = 1,
SnakeCase = 2,
}
public static class SurrealNamingPolicyExtensions
{
public static string Apply(this SurrealNamingPolicy policy, string name);
}
The naming strategy for translating CLR member names to SurrealDB field names when no explicit
[SurrealField(name)] is given. AsIs uses the CLR name unchanged; CamelCase lowercases the first
character (FirstName → firstName); SnakeCase inserts underscores at case boundaries and
lowercases (FirstName → first_name). Apply throws ArgumentNullException for a null name.
Query Responses
SurrealQueryResponse / SurrealStatementResult
public sealed record SurrealStatementResult(bool IsSuccess, SurrealValue Value, string? Error, TimeSpan Duration);
public sealed class SurrealQueryResponse
{
public SurrealQueryResponse(IReadOnlyList<SurrealStatementResult> statements, ISurrealTypeMapper mapper);
public IReadOnlyList<SurrealStatementResult> Statements { get; }
public IReadOnlyList<T?> GetResults<T>(int statement = 0);
public T? GetFirstOrDefault<T>(int statement = 0);
}
SurrealStatementResult is the outcome of one statement in a multi-statement query; Value is
SurrealValue.None on failure. SurrealQueryResponse wraps the per-statement results returned by
ISurrealClient.QueryAsync and materializes them through the injected ISurrealTypeMapper.
GetResults<T>(statement)maps array results element-wise, wraps a single (non-array, non-NONE/NULL) value as a one-element list, and mapsNONE/NULLto an empty list. ThrowsArgumentOutOfRangeExceptionfor an out-of-rangestatementindex, andSurrealDbQueryException(withStatementIndexandServerMessageset) when the target statement failed on the server.GetFirstOrDefault<T>(statement)returns the first materialized result, ordefaultwhen the result list is empty.- Both materialization methods delegate to the injected
ISurrealTypeMapperand are unannotated for trimming/AOT: with a source-generated mapper the whole path is reflection-free, and the reflection-based default mapper declares its unsafety on its own constructor.
Patch Operations
SurrealPatchOperation / PatchOperationType
public enum PatchOperationType
{
Add = 0,
Remove = 1,
Replace = 2,
Copy = 3,
Move = 4,
Test = 5,
}
public sealed record SurrealPatchOperation
{
public PatchOperationType Type { get; }
public string Path { get; }
public SurrealValue Value { get; } // SurrealValue.None when not applicable
public string? From { get; }
public static SurrealPatchOperation Add(string path, SurrealValue value);
public static SurrealPatchOperation Remove(string path);
public static SurrealPatchOperation Replace(string path, SurrealValue value);
public static SurrealPatchOperation Copy(string from, string path);
public static SurrealPatchOperation Move(string from, string path);
public static SurrealPatchOperation Test(string path, SurrealValue value);
}
An RFC 6902 JSON Patch operation, consumed by ISurrealClient.PatchAsync. There is no public
constructor — operations are built exclusively through the six validating factory methods, each
mapping to one JSON Patch verb. Every path (and from, for Copy/Move) is validated to be
non-null and to start with /; violating either throws ArgumentNullException or
ArgumentException ("A JSON Pointer path must start with '/'.") from the private constructor.
Live Queries
LiveAction
public enum LiveAction
{
Create = 0,
Update = 1,
Delete = 2,
Killed = 3,
}
The kind of change reported by a live query. Killed signals that the stream ends after this
notification.
LiveNotification
public readonly record struct LiveNotification(Guid QueryId, LiveAction Action, SurrealValue Record, SurrealValue Result);
A raw live-query notification as delivered by the transport (ISurrealConnection.SubscribeAsync).
Record is the affected record's id as a SurrealValueKind.RecordId value, and Result is the
affected record (or patch payload); both are SurrealValue.None for LiveAction.Killed.
LiveQueryChange<T>
public sealed record LiveQueryChange<T>(LiveAction Action, RecordId? Id, T? Record);
A typed live-query change, produced by ISurrealClient.LiveAsync<T> from the raw
LiveNotification stream. Id and Record are null for LiveAction.Killed; for Delete,
Record carries the full deleted record.
Exceptions
All exceptions derive from SurrealDbException, itself derived from System.Exception, and each
follows the standard three-constructor shape ((), (string message),
(string message, Exception innerException)) plus any type-specific properties noted below.
public class SurrealDbException : Exception;
Base type for every exception this ecosystem throws.
public sealed class SurrealDbConfigurationException : SurrealDbException;
Thrown when SurrealDbOptions are invalid — see SurrealDbOptions.Validate() above for every
condition that triggers it.
public sealed class SurrealDbConnectionException : SurrealDbException
{
public bool IsRetriable { get; init; }
}
Thrown when the connection to the server fails or is lost. IsRetriable indicates whether retrying
the operation may succeed (e.g. after a reconnect).
public sealed class SurrealDbAuthenticationException : SurrealDbException;
Thrown when authentication or authorization fails (e.g. from ISurrealClient.SignInAsync).
public sealed class SurrealDbRpcException : SurrealDbException
{
public long ErrorCode { get; init; }
public SurrealValue Details { get; init; } // SurrealValue.None when absent
public SurrealValue Cause { get; init; } // SurrealValue.None when absent
}
Thrown when the server answers an RPC request with an error object (see ISurrealConnection.SendAsync).
public sealed class SurrealDbQueryException : SurrealDbException
{
public int StatementIndex { get; init; }
public string? ServerMessage { get; init; }
}
Thrown when a statement inside a multi-statement query response failed on the server (see
SurrealQueryResponse.GetResults<T>).
public sealed class SurrealDbSerializationException : SurrealDbException;
Thrown when a value cannot be serialized or deserialized.
public sealed class SurrealDbCapabilityException : SurrealDbException;
Thrown when the active transport does not support the requested operation (e.g. live queries over an
HTTP-only transport — see ISurrealClient.LiveAsync and ISurrealConnection.SubscribeAsync).
| 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
- No dependencies.
NuGet packages (16)
Showing the top 5 NuGet packages that depend on Palm.SurrealDB.Net.Abstractions:
| Package | Downloads |
|---|---|
|
Palm.SurrealDB.Net.Serialization
CBOR serialization engine and reflection-based type mapper for the Palm.SurrealDB .NET ecosystem. |
|
|
Palm.SurrealDB.Net.Protocol
WebSocket and HTTP RPC transports for the Palm.SurrealDB .NET ecosystem. |
|
|
Palm.SurrealDB.Net.SurrealQL
SurrealQL abstract syntax tree and emitter for the Palm.SurrealDB .NET ecosystem. |
|
|
Palm.SurrealDB.Net
SurrealDB client for .NET — the Palm.SurrealDB ecosystem's primary package. |
|
|
Palm.SurrealDB.Net.Diagnostics
ActivitySource, EventSource, DiagnosticListener and ILogger instrumentation for the Palm.SurrealDB.Net client. |
GitHub repositories
This package is not used by any popular GitHub repositories.