Skip to content

Persistent service serialization ignores CircuitOptions.JsonTypeInfoResolvers when reflection is disabled #69325

Description

@JamesNK

Description

Blazor persistent service serialization does not use the JSON metadata resolvers configured through CircuitOptions.JsonTypeInfoResolvers. As a result, pausing an interactive server circuit throws when System.Text.Json reflection-based serialization is disabled, including in Native AOT applications.

This was observed with .NET 11.0.0-rc.1.26425.128.

Reproduction

Configure an interactive server application with reflection serialization disabled and a generated context:

<PropertyGroup>
  <PublishAot>true</PublishAot>
  <JsonSerializerIsReflectionEnabledByDefault>false</JsonSerializerIsReflectionEnabledByDefault>
</PropertyGroup>
services.AddRazorComponents().AddInteractiveServerComponents(options =>
{
#pragma warning disable ASPNETCORE9004
    options.JsonTypeInfoResolvers.Add(AppJsonSerializerContext.Default);
#pragma warning restore ASPNETCORE9004
});

Start an interactive server circuit and pause it. The persistent service callback fails:

System.InvalidOperationException: Reflection-based serialization has been disabled for this application. Either use the source generator APIs or explicitly configure the 'JsonSerializerOptions.TypeInfoResolver' property.
   at System.Text.Json.ThrowHelper.ThrowInvalidOperationException_JsonSerializerIsReflectionDisabled()
   at System.Text.Json.JsonSerializerOptions.ConfigureForJsonSerializer()
   at System.Text.Json.JsonSerializer.GetTypeInfo(JsonSerializerOptions options, Type inputType)
   at System.Text.Json.JsonSerializer.SerializeToUtf8Bytes(Object value, Type inputType, JsonSerializerOptions options)
   at Microsoft.AspNetCore.Components.PersistentComponentState.PersistAsJson(String key, Object instance, Type type)
   at Microsoft.AspNetCore.Components.Infrastructure.PersistentServicesRegistry.PersistInstanceState(Object instance, Type type, PersistentComponentState state)
   at Microsoft.AspNetCore.Components.Infrastructure.PersistentServicesRegistry.<>c__DisplayClass14_1.<RegisterForPersistence>b__1()
   at Microsoft.AspNetCore.Components.Infrastructure.ComponentStatePersistenceManager.<TryPauseAsync>g__TryExecuteCallback|21_0(Func`1 callback, ILogger`1 logger)

Cause

PersistentServicesRegistry.PersistInstanceState calls the non-generic PersistentComponentState.PersistAsJson(key, value, propertyType) overload:

https://github.com/dotnet/dotnet/blob/3551975be08744f0418857c5bed8ab1545c5dd47/src/aspnetcore/src/Components/Components/src/PersistentState/PersistentServicesRegistry.cs#L119-L129

That overload always uses the internal JsonSerializerOptionsProvider.Options:

https://github.com/dotnet/dotnet/blob/3551975be08744f0418857c5bed8ab1545c5dd47/src/aspnetcore/src/Components/Components/src/PersistentComponentState.cs#L127-L138

It therefore cannot see the source-generated contexts in CircuitOptions.JsonTypeInfoResolvers.

For the persistent services registered by AddRazorComponents, the generated serialization context needs metadata for these [PersistentState] property types:

[JsonSerializable(typeof(string))]
[JsonSerializable(typeof(Microsoft.AspNetCore.Components.Forms.AntiforgeryRequestToken))]
internal partial class AppJsonSerializerContext : JsonSerializerContext;

Specifically:

  • ResourceCollectionProvider.ResourceCollectionUrl requires System.String.
  • DefaultAntiforgeryStateProvider.CurrentToken requires Microsoft.AspNetCore.Components.Forms.AntiforgeryRequestToken.
  • Application-registered persistent services additionally require metadata for each property type marked with [PersistentState].

Adding these types to the application context does not help today because the persistent service path does not consume that context.

Expected behavior

Persistent service persistence and restoration should use the configured CircuitOptions.JsonTypeInfoResolvers, or expose an equivalent supported options/serializer hook, so Native AOT applications can provide generated metadata for all persistent property types.

Workaround limitations

Setting JsonSerializerIsReflectionEnabledByDefault=true avoids the runtime exception, but it is not a viable Native AOT workaround. It roots DefaultJsonTypeInfoResolver through Microsoft.AspNetCore.Http.Json.JsonOptions.CreateDefaultTypeResolver and produces IL2026/IL3050 trim and AOT analysis errors. Removing the framework persistent service registrations also loses resource collection and antiforgery state across persistence.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions