File: Routing\QueryParameterValueSupplier.cs
Web Access
Project: src\src\Components\Components\src\Microsoft.AspNetCore.Components.csproj (Microsoft.AspNetCore.Components)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System.Runtime.InteropServices;
using Microsoft.AspNetCore.Internal;
 
namespace Microsoft.AspNetCore.Components.Routing;
 
internal sealed class QueryParameterValueSupplier
{
    private readonly Dictionary<ReadOnlyMemory<char>, StringSegmentAccumulator> _queryParameterValuesByName = new(QueryParameterNameComparer.Instance);
 
    public void ReadParametersFromQuery(ReadOnlyMemory<char> query)
    {
        _queryParameterValuesByName.Clear();
 
        var queryStringEnumerable = new QueryStringEnumerable(query);
 
        foreach (var suppliedPair in queryStringEnumerable)
        {
            var decodedName = suppliedPair.DecodeName();
            var decodedValue = suppliedPair.DecodeValue();
 
            // This is safe because we don't mutate the dictionary while the ref local is in scope.
            ref var values = ref CollectionsMarshal.GetValueRefOrAddDefault(_queryParameterValuesByName, decodedName, out _);
            values.Add(decodedValue);
        }
    }
 
    public object? GetQueryParameterValue(Type targetType, string queryParameterName)
    {
        var isArray = targetType.IsArray;
        var elementType = isArray ? targetType.GetElementType()! : targetType;
 
        if (!UrlValueConstraint.TryGetByTargetType(elementType, out var parser))
        {
            throw new InvalidOperationException($"Querystring values cannot be parsed as type '{elementType}'.");
        }
 
        var values = _queryParameterValuesByName.GetValueOrDefault(queryParameterName.AsMemory());
 
        if (isArray)
        {
            return parser.ParseMultiple(values, queryParameterName);
        }
 
        if (values.Count > 0)
        {
            return parser.Parse(values[0].Span, queryParameterName);
        }
 
        return default;
    }
}