File: TypeForwardingActivator.cs
Web Access
Project: src\src\DataProtection\DataProtection\src\Microsoft.AspNetCore.DataProtection.csproj (Microsoft.AspNetCore.DataProtection)
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
 
using System;
using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Abstractions;
 
namespace Microsoft.AspNetCore.DataProtection;
 
#pragma warning disable CA1852 // Seal internal types
internal class TypeForwardingActivator : SimpleActivator
#pragma warning restore CA1852 // Seal internal types
{
    private const string OldNamespace = "Microsoft.AspNet.DataProtection";
    private const string CurrentNamespace = "Microsoft.AspNetCore.DataProtection";
    private readonly ILogger _logger;
 
    public TypeForwardingActivator(IServiceProvider services)
        : this(services, NullLoggerFactory.Instance)
    {
    }
 
    public TypeForwardingActivator(IServiceProvider services, ILoggerFactory loggerFactory)
        : base(services)
    {
        _logger = loggerFactory.CreateLogger(typeof(TypeForwardingActivator));
    }
 
    public override object CreateInstance([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type expectedBaseType, string originalTypeName)
        => CreateInstance(expectedBaseType, originalTypeName, out var _);
 
    // for testing
    [UnconditionalSuppressMessage("Trimmer", "IL2057", Justification = "Type.GetType is only used with forwarded types that are referenced by DataProtection assembly.")]
    internal object CreateInstance([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type expectedBaseType, string originalTypeName, out bool forwarded)
    {
        if (TryForwardTypeName(originalTypeName, out var forwardedTypeName))
        {
            var type = Type.GetType(forwardedTypeName, false);
            if (type != null)
            {
                if (_logger.IsEnabled(LogLevel.Debug))
                {
                    _logger.LogDebug("Forwarded activator type request from {FromType} to {ToType}",
                        originalTypeName,
                        forwardedTypeName);
                }
                forwarded = true;
                return base.CreateInstance(expectedBaseType, forwardedTypeName);
            }
        }
 
        forwarded = false;
        return base.CreateInstance(expectedBaseType, originalTypeName);
    }
 
    internal static bool TryForwardTypeName(string originalTypeName, out string forwardedTypeName)
    {
        forwardedTypeName = originalTypeName;
 
        var candidate = false;
        if (originalTypeName.Contains(OldNamespace))
        {
            candidate = true;
            forwardedTypeName = originalTypeName.Replace(OldNamespace, CurrentNamespace);
        }
 
        if (candidate || forwardedTypeName.StartsWith(CurrentNamespace + ".", StringComparison.Ordinal))
        {
            candidate = true;
            forwardedTypeName = RemoveVersionFromAssemblyName(forwardedTypeName);
        }
 
        return candidate;
    }
 
    protected static string RemoveVersionFromAssemblyName(string forwardedTypeName)
    {
        // Type, Assembly, Version={Version}, Culture={Culture}, PublicKeyToken={Token}
 
        var versionStartIndex = forwardedTypeName.IndexOf(", Version=", StringComparison.Ordinal);
        while (versionStartIndex != -1)
        {
            var versionEndIndex = forwardedTypeName.IndexOf(',', versionStartIndex + ", Version=".Length);
 
            if (versionEndIndex == -1)
            {
                // No end index, so are done and can remove the rest
                return forwardedTypeName.Substring(0, versionStartIndex);
            }
 
            forwardedTypeName = forwardedTypeName.Remove(versionStartIndex, versionEndIndex - versionStartIndex);
            versionStartIndex = forwardedTypeName.IndexOf(", Version=", StringComparison.Ordinal);
        }
 
        // No version left
        return forwardedTypeName;
    }
}