Skip to content

Commit 450d7ef

Browse files
Enable AOT compatibility and use source-generated JSON
Added `<IsAotCompatible>true</IsAotCompatible>` to the project file for AOT compatibility. Updated `Microsoft.Extensions.Logging.Abstractions` to version `9.0.9` for `net9.0`. Replaced static `JsonSerializerOptions` with a source-generated `GamesClientJsonContext` for JSON serialization in `GamesClient.cs`. Refactored all HTTP client methods to use `GamesClientJsonContext` for improved performance and maintainability. Introduced a new partial class `GamesClientJsonContext` with `[JsonSourceGenerationOptions]` and `[JsonSerializable]` attributes to define serializable types. These changes enhance performance, reduce runtime overhead, and improve compatibility with AOT scenarios.
1 parent cfd0f1b commit 450d7ef

File tree

2 files changed

+31
-16
lines changed

2 files changed

+31
-16
lines changed

src/clients/Codebreaker.GameAPIs.Client/Codebreaker.GameAPIs.Client.csproj

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
<PackageIcon>codebreaker.jpeg</PackageIcon>
1818
<Version>3.9.0</Version>
1919
<ManagePackageVersionsCentrally>false</ManagePackageVersionsCentrally>
20+
<IsAotCompatible>true</IsAotCompatible>
2021
</PropertyGroup>
2122

2223
<ItemGroup>
@@ -30,7 +31,7 @@
3031
</ItemGroup>
3132

3233
<ItemGroup Condition="'$(TargetFramework)'=='net9.0'">
33-
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.5" />
34-
</ItemGroup>
34+
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.9" />
35+
</ItemGroup>
3536

3637
</Project>

src/clients/Codebreaker.GameAPIs.Client/GamesClient.cs

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,6 @@ public class GamesClient(HttpClient httpClient, ILogger<GamesClient> logger) : I
99
internal const string Version = "1.0.0";
1010
internal static ActivitySource ActivitySource { get; } = new ActivitySource(ActivitySourceName, Version);
1111

12-
private readonly static JsonSerializerOptions s_jsonOptions = new()
13-
{
14-
PropertyNameCaseInsensitive = true
15-
};
16-
1712
/// <summary>
1813
/// Starts a new game.
1914
/// </summary>
@@ -30,9 +25,9 @@ public class GamesClient(HttpClient httpClient, ILogger<GamesClient> logger) : I
3025
try
3126
{
3227
CreateGameRequest createGameRequest = new(gameType, playerName);
33-
var response = await httpClient.PostAsJsonAsync("/games", createGameRequest, s_jsonOptions, cancellationToken);
28+
var response = await httpClient.PostAsJsonAsync("/games", createGameRequest, GamesClientJsonContext.Default.CreateGameRequest, cancellationToken);
3429
response.EnsureSuccessStatusCode();
35-
var gameResponse = await response.Content.ReadFromJsonAsync<CreateGameResponse>(s_jsonOptions, cancellationToken) ?? throw new InvalidOperationException();
30+
var gameResponse = await response.Content.ReadFromJsonAsync(GamesClientJsonContext.Default.CreateGameResponse, cancellationToken) ?? throw new InvalidOperationException();
3631

3732
logger.GameCreated(gameResponse.Id);
3833
activity?.GameCreatedEvent(gameResponse.Id.ToString(), gameResponse.GameType.ToString());
@@ -60,7 +55,7 @@ public async Task CancelGameAsync(Guid id, string playerName, GameType gameType,
6055
try
6156
{
6257
var request = new UpdateGameRequest(id, gameType, playerName, 0, true);
63-
var response = await httpClient.PatchAsJsonAsync($"/games/{id}", request, s_jsonOptions, cancellationToken);
58+
var response = await httpClient.PatchAsJsonAsync($"/games/{id}", request, GamesClientJsonContext.Default.UpdateGameRequest, cancellationToken);
6459
response.EnsureSuccessStatusCode();
6560
logger.GameCanceled(id);
6661
activity?.GameCanceledEvent(id.ToString());
@@ -90,11 +85,11 @@ public async Task<GameInfo> RevealGameAsync(Guid id, string playerName, GameType
9085
{
9186
// First cancel/end the game
9287
var request = new UpdateGameRequest(id, gameType, playerName, 0, End: true);
93-
var cancelResponse = await httpClient.PatchAsJsonAsync($"/games/{id}", request, s_jsonOptions, cancellationToken);
88+
var cancelResponse = await httpClient.PatchAsJsonAsync($"/games/{id}", request, GamesClientJsonContext.Default.UpdateGameRequest, cancellationToken);
9489
cancelResponse.EnsureSuccessStatusCode();
9590

9691
// Then get the full game details
97-
var gameResponse = await httpClient.GetFromJsonAsync<GameInfo>($"/games/{id}", s_jsonOptions, cancellationToken)
92+
var gameResponse = await httpClient.GetFromJsonAsync($"/games/{id}", GamesClientJsonContext.Default.GameInfo, cancellationToken)
9893
?? throw new InvalidOperationException($"Could not retrieve game with ID {id}");
9994

10095
int moveCount = gameResponse.Moves?.Count ?? 0;
@@ -132,9 +127,9 @@ public async Task<GameInfo> RevealGameAsync(Guid id, string playerName, GameType
132127
{
133128
GuessPegs = guessPegs
134129
};
135-
var response = await httpClient.PatchAsJsonAsync($"/games/{id}", updateGameRequest, s_jsonOptions, cancellationToken);
130+
var response = await httpClient.PatchAsJsonAsync($"/games/{id}", updateGameRequest, GamesClientJsonContext.Default.UpdateGameRequest, cancellationToken);
136131
response.EnsureSuccessStatusCode();
137-
var moveResponse = await response.Content.ReadFromJsonAsync<UpdateGameResponse>(s_jsonOptions, cancellationToken)
132+
var moveResponse = await response.Content.ReadFromJsonAsync(GamesClientJsonContext.Default.UpdateGameResponse, cancellationToken)
138133
?? throw new InvalidOperationException();
139134

140135
logger.MoveSet(id, moveResponse.MoveNumber);
@@ -168,7 +163,7 @@ public async Task<GameInfo> RevealGameAsync(Guid id, string playerName, GameType
168163
GameInfo? game;
169164
try
170165
{
171-
game = await httpClient.GetFromJsonAsync<GameInfo>($"/games/{id}", s_jsonOptions, cancellationToken);
166+
game = await httpClient.GetFromJsonAsync($"/games/{id}", GamesClientJsonContext.Default.GameInfo, cancellationToken);
172167
logger.GameReceived(id, game?.EndTime != null, game?.LastMoveNumber ?? 0);
173168
}
174169
catch (HttpRequestException ex) when (ex.StatusCode == HttpStatusCode.NotFound)
@@ -198,7 +193,7 @@ public async Task<IEnumerable<GameInfo>> GetGamesAsync(GamesQuery query, Cancell
198193
try
199194
{
200195
string urlQuery = query.AsUrlQuery();
201-
IEnumerable<GameInfo> games = (await httpClient.GetFromJsonAsync<IEnumerable<GameInfo>>($"/games{urlQuery}", s_jsonOptions, cancellationToken)) ?? [];
196+
IEnumerable<GameInfo> games = (await httpClient.GetFromJsonAsync($"/games{urlQuery}", GamesClientJsonContext.Default.IEnumerableGameInfo, cancellationToken)) ?? [];
202197

203198
int gameCount = games.Count();
204199
logger.GamesReceived(urlQuery, gameCount);
@@ -219,3 +214,22 @@ public async Task<IEnumerable<GameInfo>> GetGamesAsync(GamesQuery query, Cancell
219214
}
220215
}
221216
}
217+
218+
219+
[JsonSourceGenerationOptions(WriteIndented = true, PropertyNameCaseInsensitive = true)]
220+
[JsonSerializable(typeof(CreateGameRequest))]
221+
[JsonSerializable(typeof(CreateGameResponse))]
222+
[JsonSerializable(typeof(UpdateGameRequest))]
223+
[JsonSerializable(typeof(UpdateGameResponse))]
224+
[JsonSerializable(typeof(GameInfo))]
225+
[JsonSerializable(typeof(MoveInfo))]
226+
[JsonSerializable(typeof(GamesQuery))]
227+
[JsonSerializable(typeof(GameType))]
228+
[JsonSerializable(typeof(IEnumerable<GameInfo>))]
229+
[JsonSerializable(typeof(IDictionary<string, string[]>))]
230+
[JsonSerializable(typeof(IDictionary<string, IEnumerable<string>>))]
231+
[JsonSerializable(typeof(ICollection<MoveInfo>))]
232+
internal partial class GamesClientJsonContext : JsonSerializerContext
233+
{
234+
235+
}

0 commit comments

Comments
 (0)