بنقرة واحدة
csharp-server
SpacetimeDB C# server module SDK reference. Use when writing tables, reducers, or module logic in C#.
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
SpacetimeDB C# server module SDK reference. Use when writing tables, reducers, or module logic in C#.
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
Understand SpacetimeDB architecture and core concepts. Use when learning SpacetimeDB or making architectural decisions.
SpacetimeDB C++ server module SDK reference. Use when writing tables, reducers, or module logic in C++.
SpacetimeDB CLI reference for initializing projects, building modules, publishing databases, querying data, and managing servers
SpacetimeDB C#/.NET client SDK reference. Use when building C# clients that connect to SpacetimeDB (console, desktop, or any .NET app).
SpacetimeDB Rust server module SDK reference. Use when writing tables, reducers, or module logic in Rust.
SpacetimeDB TypeScript/React client SDK reference. Use when building web clients that connect to SpacetimeDB.
| name | csharp-server |
| description | SpacetimeDB C# server module SDK reference. Use when writing tables, reducers, or module logic in C#. |
| license | Apache-2.0 |
| metadata | {"author":"clockworklabs","version":"2.0","role":"server","language":"csharp","cursor_globs":"**/*.cs","cursor_always_apply":true} |
using SpacetimeDB;
All tables, types, and reducers go inside a static partial class:
using SpacetimeDB;
public static partial class Module
{
// Tables, types, and reducers here
}
[SpacetimeDB.Table(...)] on a public partial struct. Accessor should be PascalCase:
[SpacetimeDB.Table(Accessor = "Entity", Public = true)]
public partial struct Entity
{
[PrimaryKey]
[AutoInc]
public ulong Id;
public Identity Owner;
public string Name;
public bool Active;
}
Options: Accessor = "PascalCase" (recommended), Public = true, Scheduled = nameof(ReducerFn), ScheduledAt = nameof(field), Event = true
ctx.Db accessors use the Accessor name: ctx.Db.Entity, ctx.Db.Record.
| C# type | Notes |
|---|---|
byte / ushort / uint / ulong | unsigned integers |
U128 / U256 | large unsigned integers (SpacetimeDB types) |
sbyte / short / int / long | signed integers |
I128 / I256 | large signed integers (SpacetimeDB types) |
float / double | floats |
bool | boolean |
string | text |
List<T> | list/array |
Identity | user identity |
ConnectionId | connection handle |
Timestamp | server timestamp (microseconds since epoch) |
TimeDuration | duration in microseconds |
Uuid | UUID |
[PrimaryKey] // primary key
[AutoInc] // auto-increment (use 0 as placeholder on insert)
[Unique] // unique constraint
[SpacetimeDB.Index.BTree] // btree index (enables .Filter() on this column)
Prefer [SpacetimeDB.Index.BTree] inline for single-column. Multi-column uses struct-level:
// Inline (preferred for single-column):
[SpacetimeDB.Index.BTree]
public ulong AuthorId;
// Access: ctx.Db.Post.AuthorId.Filter(authorId)
// Multi-column (struct-level):
[SpacetimeDB.Table(Accessor = "Membership")]
[SpacetimeDB.Index.BTree(Accessor = "ByGroupUser", Columns = new[] { nameof(GroupId), nameof(UserId) })]
public partial struct Membership { public ulong GroupId; public Identity UserId; ... }
When you frequently look up rows by multiple columns, prefer a multi-column index over filtering by one column and looping over the results.
[SpacetimeDB.Reducer]
public static void CreateEntity(ReducerContext ctx, string name, int age)
{
ctx.Db.Entity.Insert(new Entity { Owner = ctx.Sender, Name = name, Age = age, Active = true });
}
// No arguments:
[SpacetimeDB.Reducer]
public static void DoReset(ReducerContext ctx) { ... }
ctx.Db.Entity.Insert(new Entity { Name = "Sample" }); // Insert
ctx.Db.Entity.Id.Find(entityId); // Find by PK → Entity? (nullable)
ctx.Db.Entity.Identity.Find(ctx.Sender); // Find by unique column → Entity?
ctx.Db.Item.AuthorId.Filter(authorId); // Filter by index → IEnumerable<Item>
ctx.Db.Entity.Iter(); // All rows → IEnumerable<Entity>
ctx.Db.Entity.Count; // Count rows
ctx.Db.Entity.Id.Update(existing with { Name = newName }); // Update by PK
ctx.Db.Entity.Id.Delete(entityId); // Delete by PK
Note: Filter/Iter return enumerables. Use .ToList() if you need to sort or mutate.
The pattern is ctx.Db.{Accessor}.{ColumnName}.{Method}(value) for all indexed column operations.
[SpacetimeDB.Reducer(ReducerKind.Init)]
public static void OnInit(ReducerContext ctx) { ... }
[SpacetimeDB.Reducer(ReducerKind.ClientConnected)]
public static void OnConnect(ReducerContext ctx) { ... }
[SpacetimeDB.Reducer(ReducerKind.ClientDisconnected)]
public static void OnDisconnect(ReducerContext ctx) { ... }
// Anonymous view (same result for all clients):
[SpacetimeDB.View(Accessor = "ActiveUsers", Public = true)]
public static List<Entity> ActiveUsers(AnonymousViewContext ctx)
{
return ctx.Db.Entity.Iter().Where(e => e.Active).ToList();
}
// Per-user view:
[SpacetimeDB.View(Accessor = "MyProfile", Public = true)]
public static Entity? MyProfile(ViewContext ctx)
{
return ctx.Db.Entity.Identity.Find(ctx.Sender) as Entity?;
}
ReducerContext is the single source of sender identity, deterministic time, and deterministic randomness inside a reducer. Always go through ctx for these. Standard library clocks and random sources are not available in modules.
// Auth: ctx.Sender is the caller's Identity
if (row.Owner != ctx.Sender)
throw new Exception("unauthorized");
// Server timestamp (deterministic per reducer call)
ctx.Db.Item.Insert(new Item { CreatedAt = ctx.Timestamp, .. });
// Timestamp arithmetic
var expiry = ctx.Timestamp + new TimeDuration(delayMicros);
// Deterministic RNG
int roll = ctx.Rng.Next(1, 7); // [1, 7): inclusive 1, exclusive 7
double f = ctx.Rng.NextDouble(); // [0.0, 1.0)
// Client: Timestamp → milliseconds since epoch
timestamp.MicrosecondsSinceUnixEpoch / 1000
[SpacetimeDB.Table(
Accessor = "TickTimer",
Scheduled = nameof(Tick),
ScheduledAt = nameof(ScheduledAt),
Public = true
)]
public partial struct TickTimer
{
[PrimaryKey]
[AutoInc]
public ulong ScheduledId;
public ScheduleAt ScheduledAt;
}
[SpacetimeDB.Reducer]
public static void Tick(ReducerContext ctx, TickTimer timer)
{
// timer row is auto-deleted after this reducer runs
}
// One-time: fires once at a specific time
var at = new ScheduleAt.Time(ctx.Timestamp + new TimeDuration(10_000_000));
// Repeating: fires on an interval
var at = new ScheduleAt.Interval(TimeSpan.FromSeconds(5));
ctx.Db.TickTimer.Insert(new TickTimer { ScheduledId = 0, ScheduledAt = at });
[SpacetimeDB.Type]
public enum Status { Online, Away, Offline }
[SpacetimeDB.Type]
public partial struct Point { public float X; public float Y; }
// Tagged enum (discriminated union):
[SpacetimeDB.Type]
public partial record MyUnion : SpacetimeDB.TaggedEnum<(string Text, int Number)>;
[SpacetimeDB.Table(Accessor = "Player")]
public partial struct Player
{
[PrimaryKey, AutoInc]
public ulong Id;
public string Name;
public string? Nickname;
public uint? HighScore;
}
using SpacetimeDB;
[SpacetimeDB.Table(Accessor = "Entity", Public = true)]
public partial struct Entity
{
[PrimaryKey]
public Identity Identity;
public string Name;
public bool Active;
}
[SpacetimeDB.Table(Accessor = "Record", Public = true)]
public partial struct Record
{
[PrimaryKey]
[AutoInc]
public ulong Id;
public Identity Owner;
public uint Value;
public Timestamp CreatedAt;
}
public static partial class Module
{
[SpacetimeDB.Reducer(ReducerKind.ClientConnected)]
public static void OnConnect(ReducerContext ctx)
{
var existing = ctx.Db.Entity.Identity.Find(ctx.Sender);
if (existing is not null)
ctx.Db.Entity.Identity.Update(existing.Value with { Active = true });
}
[SpacetimeDB.Reducer(ReducerKind.ClientDisconnected)]
public static void OnDisconnect(ReducerContext ctx)
{
var existing = ctx.Db.Entity.Identity.Find(ctx.Sender);
if (existing is not null)
ctx.Db.Entity.Identity.Update(existing.Value with { Active = false });
}
[SpacetimeDB.Reducer]
public static void CreateEntity(ReducerContext ctx, string name)
{
if (ctx.Db.Entity.Identity.Find(ctx.Sender) is not null)
throw new Exception("already exists");
ctx.Db.Entity.Insert(new Entity { Identity = ctx.Sender, Name = name, Active = true });
}
[SpacetimeDB.Reducer]
public static void AddRecord(ReducerContext ctx, uint value)
{
if (ctx.Db.Entity.Identity.Find(ctx.Sender) is null)
throw new Exception("not found");
ctx.Db.Record.Insert(new Record {
Id = 0,
Owner = ctx.Sender,
Value = value,
CreatedAt = ctx.Timestamp,
});
}
}