Add activator fallback to support rc1 keys

This commit is contained in:
Pavel Krymets 2016-06-02 10:00:09 -07:00
Родитель acb8732ffd
Коммит 47d3ffdddc
5 изменённых файлов: 148 добавлений и 48 удалений

Просмотреть файл

@ -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<IActivator>() ?? new SimpleActivator(serviceProvider))
: SimpleActivator.DefaultWithoutServices;
}
/// <summary>
/// A simplified default implementation of <see cref="IActivator"/> that understands
/// how to call ctors which take <see cref="IServiceProvider"/>.
/// </summary>
private sealed class SimpleActivator : IActivator
{
/// <summary>
/// A default <see cref="SimpleActivator"/> whose wrapped <see cref="IServiceProvider"/> is null.
/// </summary>
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);
}
}
}
}

Просмотреть файл

@ -24,6 +24,7 @@ namespace Microsoft.Extensions.DependencyInjection
throw new ArgumentNullException(nameof(services));
}
services.AddSingleton<IActivator, RC1ForwardingActivator>();
services.AddOptions();
services.TryAdd(DataProtectionServices.GetDefaultServices());

Просмотреть файл

@ -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);
}
}
}

Просмотреть файл

@ -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
{
/// <summary>
/// A simplified default implementation of <see cref="IActivator"/> that understands
/// how to call ctors which take <see cref="IServiceProvider"/>.
/// </summary>
internal class SimpleActivator : IActivator
{
/// <summary>
/// A default <see cref="SimpleActivator"/> whose wrapped <see cref="IServiceProvider"/> is null.
/// </summary>
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);
}
}
}

Просмотреть файл

@ -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<object>(name);
// Assert
Assert.IsType<ClassWithParameterlessCtor>(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<Exception>(()=> activator.CreateInstance<object>(name));
Assert.Contains("Microsoft.AspNet.DataProtection.Test", exception.Message);
}
private class ClassWithParameterlessCtor
{
}
}
}