Clean up usage of JsonSerializerContext (#229)

* Clean up usage of JsonSerializerContext

* review feedback
This commit is contained in:
Reuben Bond
2025-07-23 19:34:54 +00:00
committed by GitHub
parent e6aeb9a5db
commit 5e9b24e532
12 changed files with 60 additions and 123 deletions
@@ -58,16 +58,34 @@ public readonly struct ActorId : IEquatable<ActorId>
/// <summary>
/// Convert a string of the format "type/key" into an <see cref="ActorId"/>.
/// </summary>
/// <param name="actorId">The actor ID string.</param>
/// <param name="value">The actor ID string.</param>
/// <returns>An instance of <see cref="ActorId"/>.</returns>
public static ActorId Parse(string actorId)
public static ActorId Parse(string value)
{
if (!KeyValueParser.TryParse(actorId, out string? type, out string? key))
if (!TryParse(value, out var result))
{
throw new FormatException($"Invalid actor ID: '{actorId}'. Expected format is 'type/key'.");
throw new FormatException($"Invalid actor ID: '{value}'. Expected format is 'type/key'.");
}
return new ActorId(type, key);
return result;
}
private static bool TryParse(string input, out ActorId actorId)
{
if (!string.IsNullOrEmpty(input))
{
int separatorIndex = input.IndexOf('/');
if (separatorIndex >= 0)
{
var type = input.Substring(0, separatorIndex);
var key = input.Substring(separatorIndex + 1);
actorId = new ActorId(type, key);
return true;
}
}
actorId = default;
return false;
}
/// <inheritdoc />
@@ -1,33 +0,0 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
namespace Microsoft.Extensions.AI.Agents.Runtime;
/// <summary>
/// Provides extension methods for JSON serialization with source generation support.
/// </summary>
internal static class JsonSerializerExtensions
{
/// <summary>
/// Gets the JsonTypeInfo for a type, preferring the one from options if available,
/// otherwise falling back to the source-generated context.
/// </summary>
/// <typeparam name="T">The type to get JsonTypeInfo for.</typeparam>
/// <param name="options">The JsonSerializerOptions to check first.</param>
/// <param name="fallbackContext">The fallback JsonSerializerContext to use if not found in options.</param>
/// <returns>The JsonTypeInfo for the requested type.</returns>
public static JsonTypeInfo<T> GetTypeInfo<T>(this JsonSerializerOptions options, JsonSerializerContext fallbackContext)
{
// Try to get from the options first (if a context is configured)
if (options.TypeInfoResolver?.GetTypeInfo(typeof(T), options) is JsonTypeInfo<T> typeInfo)
{
return typeInfo;
}
// Fall back to the provided source-generated context
return (JsonTypeInfo<T>)fallbackContext.GetTypeInfo(typeof(T))!;
}
}
@@ -1,31 +0,0 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Diagnostics.CodeAnalysis;
namespace Microsoft.Extensions.AI.Agents.Runtime;
/// <summary>
/// Provides helper methods for parsing key-value string representations.
/// </summary>
internal static class KeyValueParser
{
/// <summary>
/// Parses a string in the format "key/value" into a tuple containing the key and value.
/// </summary>
public static bool TryParse(string input, [NotNullWhen(true)] out string? key, [NotNullWhen(true)] out string? value)
{
if (!string.IsNullOrEmpty(input))
{
int separatorIndex = input.IndexOf('/');
if (separatorIndex >= 0)
{
key = input.Substring(0, separatorIndex);
value = input.Substring(separatorIndex + 1);
return true;
}
}
key = value = null;
return false;
}
}
@@ -3,7 +3,6 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
@@ -88,9 +87,8 @@ internal sealed class ActorRuntimeBuilder : IActorRuntimeBuilder
services.AddSingleton<IActorClient, InProcessActorClient>();
services.AddSingleton<InProcessActorRuntime>(sp =>
{
var jsonSerializerOptions = sp.GetService<JsonSerializerOptions>() ?? new();
var actorStateStorage = sp.GetRequiredService<IActorStateStorage>();
return new InProcessActorRuntime(sp, this.ActorFactories, actorStateStorage, jsonSerializerOptions);
return new InProcessActorRuntime(sp, this.ActorFactories, actorStateStorage);
});
}
}
@@ -384,7 +384,7 @@ internal sealed class InProcessActorContext : IActorRuntimeContext, IAsyncDispos
{
ActorId = context.ActorId,
MessageId = entry.Request.MessageId,
Data = JsonSerializer.SerializeToElement($"Error: {exception.Message}", context._runtime.JsonSerializerOptions.GetTypeInfo<string>(ActorRuntimeJsonContext.Default)),
Data = JsonSerializer.SerializeToElement($"Error: {exception.Message}", ActorRuntimeJsonContext.Default.String),
Status = RequestStatus.Failed,
};
}
@@ -5,7 +5,6 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Diagnostics.Metrics;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using static Microsoft.Extensions.AI.Agents.Runtime.ActivityExtensions;
@@ -16,8 +15,7 @@ namespace Microsoft.Extensions.AI.Agents.Runtime;
internal sealed class InProcessActorRuntime(
IServiceProvider serviceProvider,
IReadOnlyDictionary<ActorType, Func<IServiceProvider, IActorRuntimeContext, IActor>> actorFactories,
IActorStateStorage storage,
JsonSerializerOptions jsonSerializerOptions)
IActorStateStorage storage)
{
private static readonly ActivitySource ActivitySource = new(ActorRuntimeOpenTelemetryConsts.InProcessSourceName);
private static readonly Meter Meter = new(ActorRuntimeOpenTelemetryConsts.InProcessSourceName);
@@ -38,7 +36,6 @@ internal sealed class InProcessActorRuntime(
private readonly ConcurrentDictionary<ActorId, InProcessActorContext> _actors = [];
public IActorStateStorage Storage { get; } = storage;
public JsonSerializerOptions JsonSerializerOptions { get; } = jsonSerializerOptions;
public IServiceProvider Services { get; } = serviceProvider;
internal InProcessActorContext GetOrCreateActor(ActorId actorId)
@@ -1,33 +0,0 @@
// Copyright (c) Microsoft. All rights reserved.
using System.Text.Json;
using System.Text.Json.Serialization;
using System.Text.Json.Serialization.Metadata;
namespace Microsoft.Extensions.AI.Agents.Runtime;
/// <summary>
/// Provides extension methods for JSON serialization with source generation support.
/// </summary>
internal static class JsonSerializerExtensions
{
/// <summary>
/// Gets the JsonTypeInfo for a type, preferring the one from options if available,
/// otherwise falling back to the source-generated context.
/// </summary>
/// <typeparam name="T">The type to get JsonTypeInfo for.</typeparam>
/// <param name="options">The JsonSerializerOptions to check first.</param>
/// <param name="fallbackContext">The fallback JsonSerializerContext to use if not found in options.</param>
/// <returns>The JsonTypeInfo for the requested type.</returns>
public static JsonTypeInfo<T> GetTypeInfo<T>(this JsonSerializerOptions options, JsonSerializerContext fallbackContext)
{
// Try to get from the options first (if a context is configured)
if (options.TypeInfoResolver?.GetTypeInfo(typeof(T), options) is JsonTypeInfo<T> typeInfo)
{
return typeInfo;
}
// Fall back to the provided source-generated context
return (JsonTypeInfo<T>)fallbackContext.GetTypeInfo(typeof(T))!;
}
}