Skip to content

Commit a4cb19a

Browse files
feat: add NativeAotCounter sample and simplify AOT registration API
Add AddRestateGenerated(JsonSerializerContext?) parameter to auto-configure JsonSerde when a context is provided, reducing AOT setup from two calls to one. Remove unused AddRestateAot(services, definitions, Type[]) overload. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent c6d7d63 commit a4cb19a

File tree

7 files changed

+80
-40
lines changed

7 files changed

+80
-40
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
using System.Text.Json.Serialization;
2+
3+
namespace NativeAotCounter;
4+
5+
[JsonSerializable(typeof(int))]
6+
[JsonSourceGenerationOptions(
7+
PropertyNamingPolicy = JsonKnownNamingPolicy.CamelCase,
8+
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull)]
9+
internal partial class AppJsonContext : JsonSerializerContext;
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
using Restate.Sdk;
2+
3+
namespace NativeAotCounter;
4+
5+
/// <summary>
6+
/// A Virtual Object that maintains a durable counter per key, compiled with NativeAOT.
7+
/// </summary>
8+
[VirtualObject]
9+
public sealed class CounterObject
10+
{
11+
private static readonly StateKey<int> Count = new("count");
12+
13+
[Handler]
14+
public async Task<int> Add(ObjectContext ctx, int delta)
15+
{
16+
var current = await ctx.Get(Count);
17+
var next = current + delta;
18+
ctx.Set(Count, next);
19+
return next;
20+
}
21+
22+
[Handler]
23+
public Task Reset(ObjectContext ctx)
24+
{
25+
ctx.ClearAll();
26+
return Task.CompletedTask;
27+
}
28+
29+
[SharedHandler]
30+
public async Task<int> Get(SharedObjectContext ctx)
31+
{
32+
return await ctx.Get(Count);
33+
}
34+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<IsPackable>false</IsPackable>
6+
<PublishAot>true</PublishAot>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<ProjectReference Include="../../src/Restate.Sdk/Restate.Sdk.csproj"/>
11+
<ProjectReference Include="../../src/Restate.Sdk.Generators/Restate.Sdk.Generators.csproj"
12+
OutputItemType="Analyzer"
13+
ReferenceOutputAssembly="false"/>
14+
</ItemGroup>
15+
16+
</Project>
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
using NativeAotCounter;
2+
using Restate.Sdk.Generated;
3+
using Restate.Sdk.Hosting;
4+
5+
// NativeAOT-compatible Restate endpoint with a Virtual Object.
6+
// Publish with: dotnet publish -c Release
7+
await RestateHost
8+
.CreateBuilder()
9+
.WithPort(9086)
10+
.BuildAot(services => services.AddRestateGenerated(AppJsonContext.Default))
11+
.RunAsync();
Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
using NativeAotGreeter;
2-
using Restate.Sdk;
32
using Restate.Sdk.Generated;
43
using Restate.Sdk.Hosting;
54

@@ -9,10 +8,5 @@
98
await RestateHost
109
.CreateBuilder()
1110
.WithPort(9085)
12-
.BuildAot(services =>
13-
{
14-
// Wire the source-generated JSON context for AOT-safe serialization.
15-
JsonSerde.Configure(AppJsonContext.Default.Options);
16-
services.AddRestateGenerated();
17-
})
11+
.BuildAot(services => services.AddRestateGenerated(AppJsonContext.Default))
1812
.RunAsync();

src/Restate.Sdk.Generators/Emitters/RegistrationEmitter.cs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,18 +18,24 @@ public static string Generate(ImmutableArray<ServiceInfo> services)
1818
sb.AppendLine();
1919
sb.AppendLine("/// <summary>");
2020
sb.AppendLine("/// Source-generated AOT-friendly service registration.");
21-
sb.AppendLine("/// Eliminates reflection-based DI registration for NativeAOT compatibility.");
2221
sb.AppendLine("/// </summary>");
2322
sb.AppendLine("internal static class RestateRegistration");
2423
sb.AppendLine("{");
2524
sb.AppendLine(" /// <summary>");
2625
sb.AppendLine(" /// Registers all discovered Restate services into the DI container without reflection.");
27-
sb.AppendLine(" /// Call this from <c>RestateHostBuilder.BuildAot()</c> or directly in your DI setup.");
2826
sb.AppendLine(" /// </summary>");
2927
sb.AppendLine(" public static global::Microsoft.Extensions.DependencyInjection.IServiceCollection AddRestateGenerated(");
30-
sb.AppendLine(" this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services)");
28+
sb.AppendLine(" this global::Microsoft.Extensions.DependencyInjection.IServiceCollection services,");
29+
sb.AppendLine(" global::System.Text.Json.Serialization.JsonSerializerContext? jsonContext = null)");
3130
sb.AppendLine(" {");
3231

32+
// Configure JsonSerde when a context is provided
33+
sb.AppendLine(" if (jsonContext is not null)");
34+
sb.AppendLine(" {");
35+
sb.AppendLine(" global::Restate.Sdk.JsonSerde.Configure(jsonContext.Options);");
36+
sb.AppendLine(" }");
37+
sb.AppendLine();
38+
3339
// Build the definitions array
3440
sb.AppendLine(" var definitions = new global::Restate.Sdk.Endpoint.ServiceDefinition[]");
3541
sb.AppendLine(" {");
@@ -46,7 +52,6 @@ public static string Generate(ImmutableArray<ServiceInfo> services)
4652
sb.AppendLine(" };");
4753
sb.AppendLine();
4854

49-
// Call the public AddRestateAot method (definitions only — no Type[] needed)
5055
sb.AppendLine(" global::Restate.Sdk.Hosting.RestateServiceCollectionExtensions.AddRestateAot(services, definitions);");
5156
sb.AppendLine();
5257

src/Restate.Sdk/Hosting/RestateServiceCollectionExtensions.cs

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -63,33 +63,4 @@ ServiceDefinition[] definitions
6363

6464
return services;
6565
}
66-
67-
/// <inheritdoc cref="AddRestateAot(IServiceCollection, ServiceDefinition[])" />
68-
/// <param name="services">The service collection.</param>
69-
/// <param name="definitions">Service definitions resolved from the source-generated registry.</param>
70-
/// <param name="serviceTypes">The CLR types of the service implementations for reflection-based DI registration.</param>
71-
[EditorBrowsable(EditorBrowsableState.Never)]
72-
[UnconditionalSuppressMessage(
73-
"AOT",
74-
"IL2072",
75-
Justification = "Service types are passed from source-generated code using typeof(T), which preserves the type metadata."
76-
)]
77-
[UnconditionalSuppressMessage(
78-
"Trimming",
79-
"IL2062",
80-
Justification = "Service types are passed from source-generated code using typeof(T), which preserves the type metadata."
81-
)]
82-
public static IServiceCollection AddRestateAot(
83-
this IServiceCollection services,
84-
ServiceDefinition[] definitions,
85-
Type[] serviceTypes
86-
)
87-
{
88-
AddRestateAot(services, definitions);
89-
90-
foreach (var type in serviceTypes)
91-
services.TryAddScoped(type);
92-
93-
return services;
94-
}
9566
}

0 commit comments

Comments
 (0)