diff --git a/src/Microsoft.AspNetCore.DataProtection/ActivatorExtensions.cs b/src/Microsoft.AspNetCore.DataProtection/ActivatorExtensions.cs index 3f1000ae2f..a485958fc9 100644 --- a/src/Microsoft.AspNetCore.DataProtection/ActivatorExtensions.cs +++ b/src/Microsoft.AspNetCore.DataProtection/ActivatorExtensions.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Reflection; using Microsoft.AspNetCore.Cryptography; using Microsoft.AspNetCore.DataProtection.Internal; using Microsoft.Extensions.DependencyInjection; @@ -40,52 +39,5 @@ namespace Microsoft.AspNetCore.DataProtection ? (serviceProvider.GetService() ?? new SimpleActivator(serviceProvider)) : SimpleActivator.DefaultWithoutServices; } - - /// - /// A simplified default implementation of that understands - /// how to call ctors which take . - /// - private sealed class SimpleActivator : IActivator - { - /// - /// A default whose wrapped is null. - /// - internal static readonly SimpleActivator DefaultWithoutServices = new SimpleActivator(null); - - private readonly IServiceProvider _services; - - public SimpleActivator(IServiceProvider services) - { - _services = services; - } - - public object CreateInstance(Type expectedBaseType, string implementationTypeName) - { - // Would the assignment even work? - var implementationType = Type.GetType(implementationTypeName, throwOnError: true); - expectedBaseType.AssertIsAssignableFrom(implementationType); - - // If no IServiceProvider was specified, prefer .ctor() [if it exists] - if (_services == null) - { - var ctorParameterless = implementationType.GetConstructor(Type.EmptyTypes); - if (ctorParameterless != null) - { - return Activator.CreateInstance(implementationType); - } - } - - // If an IServiceProvider was specified or if .ctor() doesn't exist, prefer .ctor(IServiceProvider) [if it exists] - var ctorWhichTakesServiceProvider = implementationType.GetConstructor(new Type[] { typeof(IServiceProvider) }); - if (ctorWhichTakesServiceProvider != null) - { - return ctorWhichTakesServiceProvider.Invoke(new[] { _services }); - } - - // Finally, prefer .ctor() as an ultimate fallback. - // This will throw if the ctor cannot be called. - return Activator.CreateInstance(implementationType); - } - } } } diff --git a/src/Microsoft.AspNetCore.DataProtection/DataProtectionServiceCollectionExtensions.cs b/src/Microsoft.AspNetCore.DataProtection/DataProtectionServiceCollectionExtensions.cs index 2f8a3ea225..36b4eabe98 100644 --- a/src/Microsoft.AspNetCore.DataProtection/DataProtectionServiceCollectionExtensions.cs +++ b/src/Microsoft.AspNetCore.DataProtection/DataProtectionServiceCollectionExtensions.cs @@ -24,6 +24,7 @@ namespace Microsoft.Extensions.DependencyInjection throw new ArgumentNullException(nameof(services)); } + services.AddSingleton(); services.AddOptions(); services.TryAdd(DataProtectionServices.GetDefaultServices()); diff --git a/src/Microsoft.AspNetCore.DataProtection/RC1ForwardingActivator.cs b/src/Microsoft.AspNetCore.DataProtection/RC1ForwardingActivator.cs new file mode 100644 index 0000000000..10c936b6ba --- /dev/null +++ b/src/Microsoft.AspNetCore.DataProtection/RC1ForwardingActivator.cs @@ -0,0 +1,42 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.Extensions.Logging; + +namespace Microsoft.AspNetCore.DataProtection +{ + internal class RC1ForwardingActivator: SimpleActivator + { + private const string From = "Microsoft.AspNet.DataProtection"; + private const string To = "Microsoft.AspNetCore.DataProtection"; + private readonly ILogger _logger; + + public RC1ForwardingActivator(IServiceProvider services) : this(services, null) + { + } + + public RC1ForwardingActivator(IServiceProvider services, ILoggerFactory loggerFactory) : base(services) + { + _logger = loggerFactory?.CreateLogger(typeof(RC1ForwardingActivator)); + } + + public override object CreateInstance(Type expectedBaseType, string implementationTypeName) + { + if (implementationTypeName.Contains(From)) + { + var forwardedImplementationTypeName = implementationTypeName.Replace(From, To); + var type = Type.GetType(forwardedImplementationTypeName, false); + if (type != null) + { + _logger?.LogDebug("Forwarded activator type request from {FromType} to {ToType}", + implementationTypeName, + forwardedImplementationTypeName); + + implementationTypeName = forwardedImplementationTypeName; + } + } + return base.CreateInstance(expectedBaseType, implementationTypeName); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.DataProtection/SimpleActivator.cs b/src/Microsoft.AspNetCore.DataProtection/SimpleActivator.cs new file mode 100644 index 0000000000..54eac601bb --- /dev/null +++ b/src/Microsoft.AspNetCore.DataProtection/SimpleActivator.cs @@ -0,0 +1,56 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Reflection; +using Microsoft.AspNetCore.DataProtection.Internal; + +namespace Microsoft.AspNetCore.DataProtection +{ + /// + /// A simplified default implementation of that understands + /// how to call ctors which take . + /// + internal class SimpleActivator : IActivator + { + /// + /// A default whose wrapped is null. + /// + internal static readonly SimpleActivator DefaultWithoutServices = new SimpleActivator(null); + + private readonly IServiceProvider _services; + + public SimpleActivator(IServiceProvider services) + { + _services = services; + } + + public virtual object CreateInstance(Type expectedBaseType, string implementationTypeName) + { + // Would the assignment even work? + var implementationType = Type.GetType(implementationTypeName, throwOnError: true); + expectedBaseType.AssertIsAssignableFrom(implementationType); + + // If no IServiceProvider was specified, prefer .ctor() [if it exists] + if (_services == null) + { + var ctorParameterless = implementationType.GetConstructor(Type.EmptyTypes); + if (ctorParameterless != null) + { + return Activator.CreateInstance(implementationType); + } + } + + // If an IServiceProvider was specified or if .ctor() doesn't exist, prefer .ctor(IServiceProvider) [if it exists] + var ctorWhichTakesServiceProvider = implementationType.GetConstructor(new Type[] { typeof(IServiceProvider) }); + if (ctorWhichTakesServiceProvider != null) + { + return ctorWhichTakesServiceProvider.Invoke(new[] { _services }); + } + + // Finally, prefer .ctor() as an ultimate fallback. + // This will throw if the ctor cannot be called. + return Activator.CreateInstance(implementationType); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.DataProtection.Test/RC1ForwardingActivatorTests.cs b/test/Microsoft.AspNetCore.DataProtection.Test/RC1ForwardingActivatorTests.cs new file mode 100644 index 0000000000..d0f01533b7 --- /dev/null +++ b/test/Microsoft.AspNetCore.DataProtection.Test/RC1ForwardingActivatorTests.cs @@ -0,0 +1,49 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.Extensions.DependencyInjection; +using Xunit; + +namespace Microsoft.AspNetCore.DataProtection +{ + public class RC1ForwardingActivatorTests + { + [Fact] + public void CreateInstance_ForwardsToNewNamespaceIfExists() + { + // Arrange + var serviceCollection = new ServiceCollection(); + serviceCollection.AddDataProtection(); + var services = serviceCollection.BuildServiceProvider(); + var activator = services.GetActivator(); + + // Act + var name = "Microsoft.AspNet.DataProtection.RC1ForwardingActivatorTests+ClassWithParameterlessCtor, Microsoft.AspNet.DataProtection.Test"; + var instance = activator.CreateInstance(name); + + // Assert + Assert.IsType(instance); + } + + [Fact] + public void CreateInstance_DoesNotForwardIfClassDoesNotExist() + { + // Arrange + var serviceCollection = new ServiceCollection(); + serviceCollection.AddDataProtection(); + var services = serviceCollection.BuildServiceProvider(); + var activator = services.GetActivator(); + + // Act & Assert + var name = "Microsoft.AspNet.DataProtection.RC1ForwardingActivatorTests+NonExistentClassWithParameterlessCtor, Microsoft.AspNet.DataProtection.Test"; + var exception = Assert.ThrowsAny(()=> activator.CreateInstance(name)); + + Assert.Contains("Microsoft.AspNet.DataProtection.Test", exception.Message); + } + + private class ClassWithParameterlessCtor + { + } + } +} \ No newline at end of file