csharp-server
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseSpacetimeDB C# SDK Reference
SpacetimeDB C# SDK 参考
Build Configuration (.NET 10)
构建配置(.NET 10)
New .NET 10 modules build as managed libraries by default. Use the CLI for WASM
builds, or select the runtime identifier explicitly when invoking dotnet:
sh
spacetime init my-project --lang csharp --dotnet-version 10新的 .NET 10 模块默认构建为托管库。对于 WASM
构建,请使用 CLI,或在调用 dotnet 时明确选择运行时标识符:
sh
spacetime init my-project --lang csharp --dotnet-version 10From the generated module directory:
From the generated module directory:
dotnet build # managed DLL
dotnet publish -c Release -r wasi-wasm # NativeAOT-LLVM WASM
spacetime build # CLI selects WASM automatically
- Do not set an unconditional `RuntimeIdentifier=wasi-wasm` in .NET 10 projects.
Keep WASM-only settings such as `PublishTrimmed`, `SelfContained`, and
`MSBuildEnableWorkloadResolver=false` conditional on that RID.
- The module must reference both `Microsoft.DotNet.ILCompiler.LLVM` and
`runtime.$(NETCoreSdkPortableRuntimeIdentifier).Microsoft.DotNet.ILCompiler.LLVM`
(version `10.0.0-*`) conditionally on `RuntimeIdentifier=wasi-wasm`.
The runtime package no longer supplies these dependencies transitively.
Keep the dotnet-experimental NuGet feed configured for WASM restores.
- `spacetime build` and `spacetime publish` select WASM automatically. .NET 10
does not require `--native-aot` or an AOT environment variable.
- Allow restore when switching between managed and WASM builds; do not reuse
`--no-restore` across modes. For multi-targeted projects, select `-f net10.0`
when publishing and restrict the LLVM 10 package references to .NET 10.
- .NET 8 behavior is unchanged: WASM/JIT by default, with `--native-aot` selecting
NativeAOT where supported. The current flag is `SPACETIMEDB_WASM_AOT`; the CLI
also sets/clears `EXPERIMENTAL_WASM_AOT` for older projects. Do not manually
define the C# compiler symbol to choose a .NET 10 build mode.
- Module dependencies use ordinary `ProjectReference` or packaged managed
assemblies. .NET 10 composition uses generated assembly descriptors, not the
dependency's standalone WASM exports; no special root/library build flag is needed.dotnet build # managed DLL
dotnet publish -c Release -r wasi-wasm # NativeAOT-LLVM WASM
spacetime build # CLI selects WASM automatically
- 不要在 .NET 10 项目中无条件设置 `RuntimeIdentifier=wasi-wasm`。
应使 `PublishTrimmed`、`SelfContained` 和
`MSBuildEnableWorkloadResolver=false` 等仅适用于 WASM 的设置以该 RID 为条件。
- 模块必须以 `RuntimeIdentifier=wasi-wasm` 为条件,同时引用
`Microsoft.DotNet.ILCompiler.LLVM` 和
`runtime.$(NETCoreSdkPortableRuntimeIdentifier).Microsoft.DotNet.ILCompiler.LLVM`
(版本 `10.0.0-*`)。运行时包不再通过传递方式提供这些依赖。
为 WASM 还原操作保留 dotnet-experimental NuGet 源的配置。
- `spacetime build` 和 `spacetime publish` 会自动选择 WASM。.NET 10
不需要 `--native-aot` 或 AOT 环境变量。
- 在托管构建和 WASM 构建之间切换时,允许执行还原操作;不要跨模式复用
`--no-restore`。对于多目标项目,发布时选择 `-f net10.0`,并将 LLVM 10
包引用限制为 .NET 10。
- .NET 8 行为保持不变:默认使用 WASM/JIT;在支持的情况下使用 `--native-aot`
选择 NativeAOT。当前标志为 `SPACETIMEDB_WASM_AOT`;CLI 也会为旧项目设置/清除
`EXPERIMENTAL_WASM_AOT`。不要手动定义 C# 编译器符号来选择 .NET 10 构建模式。
- 模块依赖项使用普通 `ProjectReference` 或打包的托管程序集。.NET 10
组合使用生成的程序集描述符,而不是依赖项独立导出的 WASM;不需要特殊的根项目/库构建标志。Module Structure
模块结构
Reducers are static methods in a ; tables are s. This reference keeps everything in one , which needs only :
static partial classpublic partial structpublic static partial class Moduleusing SpacetimeDB;Methods exported through SpacetimeDB attributes, including reducers, procedures, views, HTTP handlers, and routers, must be ; generated bindings invoke them from another class.
public staticcsharp
using SpacetimeDB;
public static partial class Module
{
[SpacetimeDB.Table(Accessor = "ScoreRecord", Public = true)]
public partial struct ScoreRecord
{
[PrimaryKey]
[AutoInc]
public ulong Id;
public Identity Owner;
public uint Value;
}
[SpacetimeDB.Reducer]
public static void AddRecord(ReducerContext ctx, uint value)
{
ctx.Db.ScoreRecord.Insert(new ScoreRecord { Id = 0, Owner = ctx.Sender, Value = value });
}
}Reducer 是 中的静态方法;表是 。本参考将所有内容放在同一个 中,因此只需要 :
static partial classpublic partial structpublic static partial class Moduleusing SpacetimeDB;通过 SpacetimeDB 特性导出的方法(包括 reducer、procedure、view、HTTP handler 和 router)必须是 ;生成的绑定会从另一个类中调用它们。
public staticcsharp
using SpacetimeDB;
public static partial class Module
{
[SpacetimeDB.Table(Accessor = "ScoreRecord", Public = true)]
public partial struct ScoreRecord
{
[PrimaryKey]
[AutoInc]
public ulong Id;
public Identity Owner;
public uint Value;
}
[SpacetimeDB.Reducer]
public static void AddRecord(ReducerContext ctx, uint value)
{
ctx.Db.ScoreRecord.Insert(new ScoreRecord { Id = 0, Owner = ctx.Sender, Value = value });
}
}Tables
表
[SpacetimeDB.Table(...)]public partial structAccessorcsharp
[SpacetimeDB.Table(Accessor = "Entity", Public = true)]
public partial struct Entity
{
[PrimaryKey]
[AutoInc]
public ulong Id;
[SpacetimeDB.Index.BTree]
public Identity Owner;
public string Name;
[SpacetimeDB.Index.BTree]
public bool Active;
}Options: (recommended), , , ,
Accessor = "PascalCase"Public = trueScheduled = nameof(ReducerFn)ScheduledAt = nameof(field)Event = truectx.DbAccessorctx.Db.Entityctx.Db.ScoreRecord[SpacetimeDB.Table(...)]public partial structAccessorcsharp
[SpacetimeDB.Table(Accessor = "Entity", Public = true)]
public partial struct Entity
{
[PrimaryKey]
[AutoInc]
public ulong Id;
[SpacetimeDB.Index.BTree]
public Identity Owner;
public string Name;
[SpacetimeDB.Index.BTree]
public bool Active;
}选项:(推荐)、、、、
Accessor = "PascalCase"Public = trueScheduled = nameof(ReducerFn)ScheduledAt = nameof(field)Event = truectx.DbAccessorctx.Db.Entityctx.Db.ScoreRecordColumn Types
列类型
| C# type | Notes |
|---|---|
| unsigned integers |
| large unsigned integers (SpacetimeDB types) |
| signed integers |
| large signed integers (SpacetimeDB types) |
| floats |
| boolean |
| text |
| list/array |
| user identity |
| connection handle |
| server timestamp (microseconds since epoch) |
| duration in microseconds |
| UUID |
Optional columns: nullable types (, )
string? Nicknameuint? HighScore| C# 类型 | 说明 |
|---|---|
| 无符号整数 |
| 大型无符号整数(SpacetimeDB 类型) |
| 有符号整数 |
| 大型有符号整数(SpacetimeDB 类型) |
| 浮点数 |
| 布尔值 |
| 文本 |
| 列表/数组 |
| 用户身份 |
| 连接句柄 |
| 服务端时间戳(自 Unix 纪元起的微秒数) |
| 以微秒为单位的时长 |
| UUID |
可选列:可空类型(、)
string? Nicknameuint? HighScoreColumn Attributes
列特性
The complete set of column attributes:
csharp
[PrimaryKey] // primary key
[AutoInc] // auto-increment (use 0 as placeholder on insert)
[Unique] // unique constraint; indexes the column, enables .Find()
[SpacetimeDB.Index.BTree] // btree index (enables .Filter() on this column)
[Default(true)] // migration-safe default for a newly appended fieldDefaults support compatible addition of a newly appended field. Do not apply to primary-key, unique, or auto-increment fields.
[Default(...)]列特性的完整集合:
csharp
[PrimaryKey] // primary key
[AutoInc] // auto-increment (use 0 as placeholder on insert)
[Unique] // unique constraint; indexes the column, enables .Find()
[SpacetimeDB.Index.BTree] // btree index (enables .Filter() on this column)
[Default(true)] // migration-safe default for a newly appended field默认值支持以兼容方式添加新追加的字段。不要将 应用于主键、唯一或自增字段。
[Default(...)]Indexes
索引
Write the index attribute fully qualified: . Prefer inline for single-column; multi-column uses struct-level:
[SpacetimeDB.Index.BTree]csharp
// 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; ... }Prefer a multi-column index over filtering by one column and looping.
完整限定地编写索引特性:。单列优先使用内联方式;多列使用结构体级别:
[SpacetimeDB.Index.BTree]csharp
// 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; ... }相比按单列过滤后循环,优先使用多列索引。
Reducers
Reducer
csharp
[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) { ... }csharp
[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) { ... }DB Operations
数据库操作
csharp
var row = ctx.Db.Entity.Insert(new Entity { Name = "Sample" }); // Insert; returns the row with AutoInc fields assigned
ctx.Db.Entity.Id.Find(entityId); // Find by PK → Entity? (nullable)
ctx.Db.Entity.Identity.Find(ctx.Sender); // Find by unique column → Entity?
if (ctx.Db.Entity.Id.Find(entityId) is { } entity) { ... } // unwrap Entity? before member access
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 PKNote: Filter/Iter return enumerables. Use if you need to sort or mutate.
.ToList()The pattern is for all indexed column operations.
ctx.Db.{Accessor}.{ColumnName}.{Method}(value)csharp
var row = ctx.Db.Entity.Insert(new Entity { Name = "Sample" }); // Insert; returns the row with AutoInc fields assigned
ctx.Db.Entity.Id.Find(entityId); // Find by PK → Entity? (nullable)
ctx.Db.Entity.Identity.Find(ctx.Sender); // Find by unique column → Entity?
if (ctx.Db.Entity.Id.Find(entityId) is { } entity) { ... } // unwrap Entity? before member access
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注意:Filter/Iter 返回可枚举对象。如果需要排序或修改,请使用 。
.ToList()所有索引列操作都遵循 模式。
ctx.Db.{Accessor}.{ColumnName}.{Method}(value)Lifecycle Hooks
生命周期钩子
csharp
[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) { ... }ctx.ConnectionIdConnectionId?csharp
[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) { ... }ctx.ConnectionIdConnectionId?Views
视图
csharp
// Anonymous view (same result for all clients):
[SpacetimeDB.View(Accessor = "ActiveUsers", Public = true)]
public static List<Entity> ActiveUsers(AnonymousViewContext ctx)
{
return ctx.Db.Entity.Active.Filter(true).ToList();
}
// Per-user view:
[SpacetimeDB.View(Accessor = "MyEntities", Public = true)]
public static List<Entity> MyEntities(ViewContext ctx)
{
return ctx.Db.Entity.Owner.Filter(ctx.Sender).ToList();
}View contexts expose read-only table handles. These support plus / on declared indexes, but not full-table . Start procedural view traversal from an indexed lookup or filter.
CountFindFilterIter()Query-builder views use , , and return directly. Use for predicates and when the result should contain right-side rows that have a matching left-side row:
ViewContextctx.FromIQuery<T>WhereRightSemijoincsharp
ctx.From.Article().Where(article => article.Published.Eq(true));
ctx.From.Subscription().RightSemijoin(
ctx.From.Account(),
(subscription, account) => subscription.AccountId.Eq(account.Id)
);Declare a procedural view primary key in its attribute: .
[SpacetimeDB.View(Accessor = "CatalogEntry", Public = true, PrimaryKey = nameof(CatalogRow.Sku))]Procedural-view primary keys are explicit schema metadata. Add one only when the view itself is required to expose a primary key; a source table's primary key is not inherited by the view.
Inclusive btree ranges use tuples, for example .
ctx.Db.Shipment.DeliverBy.Filter((new Timestamp(1_000), new Timestamp(2_000)))Views can also accept arguments (2.13+), after the context:
csharp
[SpacetimeDB.View(Accessor = "EntityById", Public = true)]
public static Entity? EntityById(AnonymousViewContext ctx, ulong id) =>
ctx.Db.Entity.Id.Find(id);Floating-point arguments and primary keys are rejected on argument-bearing views. See client skills for SQL subscription syntax and restrictions.
csharp
// Anonymous view (same result for all clients):
[SpacetimeDB.View(Accessor = "ActiveUsers", Public = true)]
public static List<Entity> ActiveUsers(AnonymousViewContext ctx)
{
return ctx.Db.Entity.Active.Filter(true).ToList();
}
// Per-user view:
[SpacetimeDB.View(Accessor = "MyEntities", Public = true)]
public static List<Entity> MyEntities(ViewContext ctx)
{
return ctx.Db.Entity.Owner.Filter(ctx.Sender).ToList();
}视图上下文提供只读表句柄。这些句柄支持 ,以及对已声明索引使用 /,但不支持全表 。过程式视图的遍历应从索引查找或过滤开始。
CountFindFilterIter()查询构建器视图使用 、,并直接返回 。使用 表达谓词;当结果应包含存在匹配左侧行的右侧行时,使用 :
ViewContextctx.FromIQuery<T>WhereRightSemijoincsharp
ctx.From.Article().Where(article => article.Published.Eq(true));
ctx.From.Subscription().RightSemijoin(
ctx.From.Account(),
(subscription, account) => subscription.AccountId.Eq(account.Id)
);在过程式视图的特性中声明主键:。
[SpacetimeDB.View(Accessor = "CatalogEntry", Public = true, PrimaryKey = nameof(CatalogRow.Sku))]过程式视图的主键是显式的 schema 元数据。仅当视图本身需要公开主键时才添加;源表的主键不会被视图继承。
包含边界范围的 btree 范围使用元组,例如 。
ctx.Db.Shipment.DeliverBy.Filter((new Timestamp(1_000), new Timestamp(2_000)))视图也可以在上下文之后接受参数(2.13+):
csharp
[SpacetimeDB.View(Accessor = "EntityById", Public = true)]
public static Entity? EntityById(AnonymousViewContext ctx, ulong id) =>
ctx.Db.Entity.Id.Find(id);带参数的视图不接受浮点参数和主键。有关 SQL 订阅语法和限制,请参阅 client skill。
Client Visibility Filters
客户端可见性过滤器
csharp
[ClientVisibilityFilter]
public static readonly Filter PrivateNoteFilter = new Filter.Sql(
"SELECT * FROM OwnedRow WHERE Owner = :sender"
);csharp
[ClientVisibilityFilter]
public static readonly Filter PrivateNoteFilter = new Filter.Sql(
"SELECT * FROM OwnedRow WHERE Owner = :sender"
);Reducer Context API
Reducer 上下文 API
ReducerContextctxcsharp
// 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)
// Timestamp → milliseconds since epoch
timestamp.MicrosecondsSinceUnixEpoch / 1000ReducerContextctxcsharp
// 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)
// Timestamp → milliseconds since epoch
timestamp.MicrosecondsSinceUnixEpoch / 1000Submodules
子模块
C# assembly composition requires SpacetimeDB/Runtime 2.13+, .NET 10, and C# 14 in the root and dependencies. NativeAOT-LLVM module builds require Windows or Linux, not macOS. Reference module assemblies via or NuGet. If libraries live beneath the root project, exclude their source from the root compile glob (). Dependencies, including transitive ones, register once in unless some module mounts them with a attribute:
ProjectReference<Compile Remove="libraries/**/*.cs" />publicNamespacecsharp
// Root Lib.cs, before namespace/type declarations:
[assembly: SpacetimeDB.Namespace(typeof(AuthLib.Marker), Accessor = "MyAuth", Name = "auth_data")]The marker is any accessible type in the dependency assembly. Root code uses and ; library code keeps and . To call a library helper, pass ( inside ; 2.14+). Passing directly works only while the library has one unambiguous instance; otherwise it throws . Helpers share the caller's transaction; namespaces are not helper security boundaries.
ctx.Db.MyAuth.Userctx.From.MyAuth.User()ctx.Db.Userctx.From.User()ctx.As.MyAuthtx.As.MyAuthWithTxctxInvalidOperationExceptionAccessorNameauth_data.<table>NameMyAuthmy_authNamepublicIn 2.14+, any module can mount its own dependencies, and the same assembly can be mounted more than once with independent tables at each path; 2.13 allows only root mounts, once per assembly. Cross-language composition is unsupported. Named dependencies cannot declare lifecycle reducers, nonempty environments, or RLS filters. Define RLS in the root with canonical qualified table names. Named-submodule routes are ignored; root handlers may delegate to library helpers. Dependencies in can contribute lifecycle reducers (subject to uniqueness), environment declarations, and routes.
publicC# 程序集组合要求根项目和依赖项均使用 SpacetimeDB/Runtime 2.13+、.NET 10 和 C# 14。NativeAOT-LLVM 模块构建需要 Windows 或 Linux,不能使用 macOS。通过 或 NuGet 引用模块程序集。如果库位于根项目下,请从根项目的编译 glob 中排除其源文件()。除非某个模块使用 特性挂载依赖项,否则包括传递依赖在内的所有依赖项都注册到 中:
ProjectReference<Compile Remove="libraries/**/*.cs" />Namespacepubliccsharp
// Root Lib.cs, before namespace/type declarations:
[assembly: SpacetimeDB.Namespace(typeof(AuthLib.Marker), Accessor = "MyAuth", Name = "auth_data")]Markerctx.Db.MyAuth.Userctx.From.MyAuth.User()ctx.Db.Userctx.From.User()ctx.As.MyAuthWithTxtx.As.MyAuthctxInvalidOperationExceptionAccessorNameauth_data.<table>NameMyAuthmy_authNamepublic在 2.14+ 中,任何模块都可以挂载自己的依赖项,并且同一程序集可以挂载多次,每个路径都有独立的表;2.13 仅允许根项目挂载,每个程序集一次。不支持跨语言组合。命名依赖项不能声明生命周期 reducer、非空环境或 RLS 过滤器。在根项目中使用规范限定表名定义 RLS。命名子模块的路由会被忽略;根 handler 可以委派给库辅助函数。位于 中的依赖项可以贡献生命周期 reducer(须满足唯一性要求)、环境声明和路由。
publicEnvironment Variables
环境变量
csharp
[SpacetimeDB.Env]
public partial struct EnvironmentSchema
{
public string API_KEY;
[SpacetimeDB.EnvValues("development", "production")]
public string MODE;
public string? LOG_LEVEL;
}
// In a root entry point:
// ctx.Env.API_KEY: string; ctx.Env.LOG_LEVEL: string?
// ctx.Env.Get("LOG_LEVEL"): string?Enable nullable annotations for optional fields. One struct declares the environment; restricts allowed strings. Colliding accessor names remain available through .
string?[SpacetimeDB.Env][EnvValues(...)]Get("KEY")See the concepts skill for shared environment and secret-handling rules.
csharp
[SpacetimeDB.Env]
public partial struct EnvironmentSchema
{
public string API_KEY;
[SpacetimeDB.EnvValues("development", "production")]
public string MODE;
public string? LOG_LEVEL;
}
// In a root entry point:
// ctx.Env.API_KEY: string; ctx.Env.LOG_LEVEL: string?
// ctx.Env.Get("LOG_LEVEL"): string?为可选的 字段启用可空注解。一个 结构体声明环境; 限制允许的字符串。即使访问器名称发生冲突,仍可通过 访问。
string?[SpacetimeDB.Env][EnvValues(...)]Get("KEY")有关共享环境和密钥处理规则,请参阅 concepts skill。
Scheduled Tables
定时表
Declare the scheduled table and its reducer in the same class so resolves:
Modulenameof(...)csharp
[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 });Scheduled reducer callbacks use the ordinary attribute. There is no ; the table's option associates the callback.
[SpacetimeDB.Reducer]ReducerKind.ScheduledScheduledTo construct a from a 128-bit numeric representation, encode it as exactly 16 little-endian bytes and call . The result is nullable and must be checked before use.
ConnectionIdConnectionId.From(bytes)在同一个 类中声明定时表及其 reducer,以便 正确解析:
Modulenameof(...)csharp
[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 });定时 reducer 回调使用普通的 特性。不存在 ;通过表的 选项关联回调。
[SpacetimeDB.Reducer]ReducerKind.ScheduledScheduled要从 128 位数值表示构造 ,请将其编码为恰好 16 个小端序字节,并调用 。结果可为空,使用前必须检查。
ConnectionIdConnectionId.From(bytes)Procedures and HTTP
Procedure 与 HTTP
Procedures are unstable APIs, so modules using them should include . They receive and may return values:
#pragma warning disable STDB_UNSTABLEProcedureContext[SpacetimeDB.Type]csharp
[SpacetimeDB.Type]
public partial struct ResultValue { public string Value; }
[SpacetimeDB.Procedure]
public static ResultValue Inspect(ProcedureContext ctx, string input) =>
new() { Value = input };Outbound HTTP is available through ; handle its success/error result before using the response. Open short database transactions with . Perform network I/O before opening the transaction, and keep only database work inside its callback.
ctx.Httpctx.WithTxcsharp
var result = ctx.Http.Get(uri);
var text = result.Match(
response => response.Body.ToStringUtf8Lossy(),
error => throw new Exception(error.Message)
);
ctx.WithTx(tx =>
{
tx.Db.ScoreRecord.Insert(new ScoreRecord { Id = 0, Owner = ctx.Sender, Value = 1 });
return 0;
});WithTxreturn 0;tx.Dbctx.DbFor non-GET requests, construct and send an directly:
HttpRequestcsharp
var result = ctx.Http.Send(new HttpRequest
{
Uri = uri,
Method = SpacetimeDB.HttpMethod.Post,
Headers = new() { new HttpHeader("content-type", "text/plain") },
Body = HttpBody.FromString(payload),
});HttpResponse.StatusCodeushortHttpHeaderValuebyte[]System.Text.Encoding.UTF8.GetString(header.Value)HttpBody.FromString(...)new HttpBody(bytes)ToBytes()ToStringUtf8Lossy()HttpBody.FromBytesToString()SpacetimeDB.HttpMethodSystem.Net.HttpScheduled procedures use the ordinary scheduled-table shape. Its name refers to a method taking plus the scheduled row, and database access inside that procedure goes through .
Scheduled[SpacetimeDB.Procedure]ProcedureContextctx.WithTxInbound HTTP uses handler attributes and one router. Handler database access also goes through :
ctx.WithTxcsharp
[SpacetimeDB.HttpHandler]
public static HttpResponse Health(HandlerContext ctx, HttpRequest request) => new(
200, HttpVersion.Http11, new(), HttpBody.FromString("ok")
);
[SpacetimeDB.HttpRouter]
public static Router Routes() => SpacetimeDB.Router.New().Get("/health", Handlers.Health);Handlers[SpacetimeDB.HttpHandler]Handlers.MethodNameProcedure 是不稳定 API,因此使用它们的模块应包含 。它们接收 ,并且可以返回 值:
#pragma warning disable STDB_UNSTABLEProcedureContext[SpacetimeDB.Type]csharp
[SpacetimeDB.Type]
public partial struct ResultValue { public string Value; }
[SpacetimeDB.Procedure]
public static ResultValue Inspect(ProcedureContext ctx, string input) =>
new() { Value = input };可通过 发起出站 HTTP 请求;使用响应前,请处理其成功/错误结果。使用 开启短数据库事务。请在开启事务前执行网络 I/O,事务回调内仅保留数据库工作。
ctx.Httpctx.WithTxcsharp
var result = ctx.Http.Get(uri);
var text = result.Match(
response => response.Body.ToStringUtf8Lossy(),
error => throw new Exception(error.Message)
);
ctx.WithTx(tx =>
{
tx.Db.ScoreRecord.Insert(new ScoreRecord { Id = 0, Owner = ctx.Sender, Value = 1 });
return 0;
});WithTxreturn 0;tx.Dbctx.Db对于非 GET 请求,直接构造并发送 :
HttpRequestcsharp
var result = ctx.Http.Send(new HttpRequest
{
Uri = uri,
Method = SpacetimeDB.HttpMethod.Post,
Headers = new() { new HttpHeader("content-type", "text/plain") },
Body = HttpBody.FromString(payload),
});HttpResponse.StatusCodeushortHttpHeaderValuebyte[]System.Text.Encoding.UTF8.GetString(header.Value)HttpBody.FromString(...)new HttpBody(bytes)ToBytes()ToStringUtf8Lossy()HttpBody.FromBytesToString()System.Net.HttpSpacetimeDB.HttpMethod定时 procedure 使用普通的定时表结构。其 名称指向一个 方法,该方法接收 和定时行,procedure 内的数据库访问通过 完成。
Scheduled[SpacetimeDB.Procedure]ProcedureContextctx.WithTx入站 HTTP 使用 handler 特性和一个 router。handler 的数据库访问同样通过 完成:
ctx.WithTxcsharp
[SpacetimeDB.HttpHandler]
public static HttpResponse Health(HandlerContext ctx, HttpRequest request) => new(
200, HttpVersion.Http11, new(), HttpBody.FromString("ok")
);
[SpacetimeDB.HttpRouter]
public static Router Routes() => SpacetimeDB.Router.New().Get("/health", Handlers.Health);Handlers[SpacetimeDB.HttpHandler]Handlers.MethodNameCustom Types
自定义类型
csharp
[SpacetimeDB.Type]
public enum Status { Online, Away, Offline }
[SpacetimeDB.Type]
public partial struct Point { public float X; public float Y; }Tagged enums (discriminated unions): a with empty body and no constructor parameters. Payloads are s:
partial record[Type] partial structcsharp
[SpacetimeDB.Type]
public partial struct Circle { public int Radius; }
[SpacetimeDB.Type]
public partial struct Rectangle { public int Width; public int Height; }
[SpacetimeDB.Type]
public partial record Shape : SpacetimeDB.TaggedEnum<(Circle Circle, Rectangle Rectangle)> { }
// Construct variants via the generated nested constructors:
var a = new Shape.Circle(new Circle { Radius = 10 });
var b = new Shape.Rectangle(new Rectangle { Width = 4, Height = 6 });csharp
[SpacetimeDB.Type]
public enum Status { Online, Away, Offline }
[SpacetimeDB.Type]
public partial struct Point { public float X; public float Y; }带标签的枚举(可辨识联合):使用空主体且没有构造函数参数的 。变体载荷是 :
partial record[Type] partial structcsharp
[SpacetimeDB.Type]
public partial struct Circle { public int Radius; }
[SpacetimeDB.Type]
public partial struct Rectangle { public int Width; public int Height; }
[SpacetimeDB.Type]
public partial record Shape : SpacetimeDB.TaggedEnum<(Circle Circle, Rectangle Rectangle)> { }
// Construct variants via the generated nested constructors:
var a = new Shape.Circle(new Circle { Radius = 10 });
var b = new Shape.Rectangle(new Rectangle { Width = 4, Height = 6 });