You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs: update multi-language integration guide for ATS-first API design
Documents the getter-only-to-async-method behavior introduced in
microsoft/aspire#16403 and adds a new 'Callback context types and the
ATS-first editor pattern' section explaining:
- How getter-only C# properties map to async methods in generated
TypeScript SDKs, while mutable-collection properties remain as
readonly getters
- The ATS-first editor/facade pattern for callback context types
(EnvironmentEditor, etc.) that should be used instead of
ExposeProperties = true on callback context classes
- How to define, export, and consume callback extension methods like
withEnvironmentCallback, withArgsCallback, and withUrls
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@@ -114,7 +114,7 @@ The export ID becomes the method name in generated SDKs. Use camelCase (e.g., `a
114
114
115
115
## Export resource types
116
116
117
-
Mark your resource types with `[AspireExport]` so the TypeScript SDK can reference them as typed handles. Set `ExposeProperties = true` to make the resource's properties accessible as get/set capabilities — most resources should include this:
117
+
Mark your resource types with `[AspireExport]` so the TypeScript SDK can reference them as typed handles. Set `ExposeProperties = true` to make all public properties accessible as capabilities, or annotate individual properties with `[AspireExport]` for fine-grained control:
118
118
119
119
```csharp title="C# — MyDatabaseResource.cs"
120
120
[AspireExport(ExposeProperties=true)]
@@ -139,24 +139,142 @@ public sealed class MyDatabaseDatabaseResource(string name, MyDatabaseResource p
139
139
{
140
140
// Your existing implementation...
141
141
}
142
-
````
142
+
```
143
143
144
144
When `ExposeProperties = true`, each public property becomes a capability in the generated SDK. Use `[AspireExportIgnore]` on properties that shouldn't be exposed.
-**Getter-only properties** (no setter, and not a mutable collection type) are generated as async methods in TypeScript: `property(): Promise<T>`.
153
+
-**Read-write properties** and **mutable-collection properties** (such as `AspireList<T>` or `AspireDict<K,V>`) are generated as `readonly` getter properties.
154
+
155
+
For example, a C# class with both kinds of property:
TypeScript AppHost authors call getter-only properties as functions:
180
+
181
+
```typescript title="TypeScript — Consuming the generated API"
182
+
const resource =awaitcontext.resource();
183
+
const tags =context.tags; // no await needed for mutable collections
184
+
```
185
+
186
+
:::tip
187
+
Prefer getter-only properties (`=> value;` or `{ get; }` without a setter) when the TypeScript side should treat the value as a read-once async operation. Use `AspireList<T>` and `AspireDict<K,V>` when TypeScript code needs to add, remove, or iterate values directly.
188
+
:::
189
+
190
+
## Callback context types and the ATS-first editor pattern
191
+
192
+
When you export a method that accepts a callback (such as `withEnvironmentCallback`, `withArgsCallback`, or `withUrls`), the callback receives a *context* object. For TypeScript compatibility, context types should follow the ATS-first design:
193
+
194
+
1. Use `[AspireExport]` (not `ExposeProperties = true`) on the context class.
195
+
2. Annotate only the properties that TypeScript callers need with individual `[AspireExport]` attributes.
196
+
3. For mutable state (environment variables, command-line arguments, URL lists), expose a small *editor* class rather than the raw collection.
197
+
198
+
### Defining an editor class
199
+
200
+
An editor wraps a mutable collection and exposes specific operations — typically `add`, `set`, or `remove` — instead of handing the raw collection to TypeScript:
Use individual `[AspireExport]` attributes on each property the TypeScript caller needs. Pass the editor as a getter-only property so TypeScript callers receive it as an async method:
The `Action<T>` delegate type is ATS-compatible. The TypeScript side receives an async function; the Aspire runtime bridges the async TypeScript call to the synchronous C# delegate on a background thread.
0 commit comments