Repository: dotnet/aspnetcore
File: src/Components/Components/src/PersistentComponentState.cs (C#)

Code before:

        if (PersistingState)
        {
            throw new InvalidOperationException("Registering a callback while persisting state is not allowed.");
        }

        var persistenceCallback = new PersistComponentStateRegistration(callback, renderMode);

        _registeredCallbacks.Add(persistenceCallback);

        return new PersistingComponentStateSubscription(_registeredCallbacks, persistenceCallback);
    }

    /// <summary>
    /// Register a callback to restore the state when the application state is being restored.
    /// </summary>
    /// <param name="callback"> The callback to invoke when the application state is being restored.</param>
    /// <param name="options">Options that control the restoration behavior.</param>
    /// <returns>A subscription that can be used to unregister the callback when disposed.</returns>
    public RestoringComponentStateSubscription RegisterOnRestoring(Action callback, RestoreOptions options)
    {
        Debug.Assert(CurrentContext != null);
        if (CurrentContext.ShouldRestore(options))
        {
            callback();
        }

        if (options.AllowUpdates)
        {
            var registration = new RestoreComponentStateRegistration(callback);
            _registeredRestoringCallbacks.Add(registration);
            return new RestoringComponentStateSubscription(_registeredRestoringCallbacks, registration);
        }

        return default;
    }

    /// <summary>
    /// Serializes <paramref name="instance"/> as JSON and persists it under the given <paramref name="key"/>.
    /// </summary>
    /// <typeparam name="TValue">The <paramref name="instance"/> type.</typeparam>
    /// <param name="key">The key to use to persist the state.</param>
    /// <param name="instance">The instance to persist.</param>
    [RequiresUnreferencedCode("JSON serialization and deserialization might require types that cannot be statically analyzed.")]
    public void PersistAsJson<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(string key, TValue instance)
    {
        ArgumentNullException.ThrowIfNull(key);

        if (!PersistingState)
        {
            throw new InvalidOperationException("Persisting state is only allowed during an OnPersisting callback.");

Review comment (performance):
The serialization now occurs unconditionally before checking if the key exists. This means expensive JSON serialization will happen even when the key already exists and an exception will be thrown. Consider checking key existence first or caching the serialized result.
```suggestion
        if (_currentState.ContainsKey(key))
        {
            throw new ArgumentException($"There is already a persisted object under the same key '{key}'");
        }
        _currentState.Add(key, JsonSerializer.SerializeToUtf8Bytes(instance, JsonSerializerOptionsProvider.Options));
```

Code after:

        _registeredCallbacks.Add(persistenceCallback);

        return new PersistingComponentStateSubscription(_registeredCallbacks, persistenceCallback);
    }

    /// <summary>
    /// Serializes <paramref name="instance"/> as JSON and persists it under the given <paramref name="key"/>.
    /// </summary>
    /// <typeparam name="TValue">The <paramref name="instance"/> type.</typeparam>
    /// <param name="key">The key to use to persist the state.</param>
    /// <param name="instance">The instance to persist.</param>
    [RequiresUnreferencedCode("JSON serialization and deserialization might require types that cannot be statically analyzed.")]
    public void PersistAsJson<[DynamicallyAccessedMembers(JsonSerialized)] TValue>(string key, TValue instance)
    {
        ArgumentNullException.ThrowIfNull(key);

        if (!PersistingState)
        {
            throw new InvalidOperationException("Persisting state is only allowed during an OnPersisting callback.");
        }

        if (!_currentState.TryAdd(key, JsonSerializer.SerializeToUtf8Bytes(instance, JsonSerializerOptionsProvider.Options)))
        {
            throw new ArgumentException($"There is already a persisted object under the same key '{key}'");
        }
    }

    [RequiresUnreferencedCode("JSON serialization and deserialization might require types that cannot be statically analyzed.")]
    internal void PersistAsJson(string key, object instance, [DynamicallyAccessedMembers(JsonSerialized)] Type type)
    {
        ArgumentNullException.ThrowIfNull(key);

        if (!PersistingState)
        {
            throw new InvalidOperationException("Persisting state is only allowed during an OnPersisting callback.");
        }

        if (!_currentState.TryAdd(key, JsonSerializer.SerializeToUtf8Bytes(instance, type, JsonSerializerOptionsProvider.Options)))
        {
            throw new ArgumentException($"There is already a persisted object under the same key '{key}'");
        }
    }

    /// <summary>
    /// Persists the provided byte array under the given key.
    /// </summary>
    /// <param name="key">The key to use to persist the state.</param>
    /// <param name="data">The byte array to persist.</param>
    internal void PersistAsBytes(string key, byte[] data)
    {