Quick Start
This guide walks through connecting to a ConvergeDB server, defining an entity kind, writing data, and subscribing to changes.
Prerequisites
Section titled “Prerequisites”- .NET 8.0 or later
- A running ConvergeDB server (default port: 3727)
Install the SDK
Section titled “Install the SDK”dotnet add package Convergence.Client# Coming soonThe Convergence.Client package includes a source generator that produces serialization code for your entity types.
Define an entity kind
Section titled “Define an entity kind”Entity kinds are defined as C# structs with attributes. The source generator handles serialization.
using Convergence.Client;
[ConvergenceEntity("Player")]public partial struct Player{ [Field(0)] public ulong Id { get; set; } [Field(1)] public uint Health { get; set; } [Field(2)] public float PosX { get; set; } [Field(3)] public float PosY { get; set; } [Field(4, MaxLength = 32)] public string Name { get; set; }
public ReadOnlyMemory<byte> EntityId { get; init; }}// Coming soonKey points:
[ConvergenceEntity("Player")]names the kind on the server.[Field(N)]assigns a stable ordinal to each field. These ordinals determine the wire layout and must never change.EntityIdis a required 32-byte property that uniquely identifies each entity within the kind.
Connect and register
Section titled “Connect and register”using Convergence.Client;
// Connect to the server — by default the server allocates a source_id// for this writer. Read client.SourceId to see what it picked.await using var client = await ConvergenceClient.ConnectAsync(new ConvergenceOptions{ Host = "127.0.0.1", Port = 3727,});Console.WriteLine($"Connected as source #{client.SourceId}");
// Register the entity kind (safe to call on every startup)KindHandle<Player> players = await client.RegisterKindAsync<Player>();// Coming soonRegisterKindAsync uses register-if-not-exists semantics. If the kind already exists with the same schema, it returns the existing KindId. If new fields have been appended, the schema is automatically migrated.
Claiming a specific source_id
Section titled “Claiming a specific source_id”If your deployment maps roles to known source_ids (e.g., inventory is always source 10), pass SourceId explicitly. A claim fails with ConnectClaimConflictException if another live connection currently holds the slot; claiming a disconnected source within its liveness deadline recovers the preserved entity set.
await using var client = await ConvergenceClient.ConnectAsync(new ConvergenceOptions{ Host = "127.0.0.1", Port = 3727, SourceId = 10,});Read-only connections
Section titled “Read-only connections”Dashboards, replicators, and other consumers that never write should open a read-only connection. Read-only clients bypass the 64-slot writer pool entirely, and the SDK rejects any write call locally before it leaves the process.
await using var reader = await ConvergenceClient.ConnectAsync(new ConvergenceOptions{ Host = "127.0.0.1", Port = 3727, ReadOnly = true,});// reader.QueryAsync / SubscribeAsync / ResolveKindAsync work.// reader.AssertAsync(...) throws ReadOnlyOperationException.Write data
Section titled “Write data”var player = new Player{ EntityId = EntityId.FromGuid(Guid.NewGuid()), Id = 42, Health = 100, PosX = 1.5f, PosY = -2.3f, Name = "Alice",};
// Assert the entity's current stateawait players.AssertAsync(player);// Coming soonAssertAsync serializes the entity, buffers it, and flushes to the server. The server creates the entity if it does not exist, or updates it if it does.
Read data
Section titled “Read data”Player? result = await players.QueryAsync(player.EntityId);if (result is { } p){ Console.WriteLine($"Found: {p.Name} at ({p.PosX}, {p.PosY})");}// Coming soonPoint reads are served entirely from the server’s in-memory entity table with no disk I/O.
Subscribe to changes
Section titled “Subscribe to changes”await foreach (var change in players.SubscribeAsync(bootstrap: true)){ Console.WriteLine($"{change.Type}: {change.Entity?.Name} (v{change.Version})");}// Coming soonWith bootstrap: true, the server first sends a snapshot of all existing entities, then transitions to live change notifications. This ensures the subscriber starts from a complete view with no gaps.
See Subscriptions and Bootstrap for the full semantics.
What’s next?
Section titled “What’s next?”- Core Concepts for the full mental model
- Schema Design for field types, arrays, and structs
- Writer’s Guide for write semantics, batching, and epochs
- Reader’s Guide for subscriptions, bootstrap, and reconnection
- Limits for hard constraints to keep in mind