From dae55edfec80090dd2aa7ba0f66f3e5009303151 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mladen=20Macanovi=C4=87?= Date: Fri, 6 Mar 2020 14:38:49 +0100 Subject: [PATCH] Add IComponentActivator --- .../Components/src/ComponentFactory.cs | 8 +++- .../src/DefaultComponentActivator.cs | 19 ++++++++++ .../Components/src/IComponentActivator.cs | 20 ++++++++++ .../Components/test/ComponentFactoryTest.cs | 37 +++++++++++++++++++ .../ComponentServiceCollectionExtensions.cs | 1 + ...MvcViewFeaturesMvcCoreBuilderExtensions.cs | 3 +- 6 files changed, 86 insertions(+), 2 deletions(-) create mode 100644 src/Components/Components/src/DefaultComponentActivator.cs create mode 100644 src/Components/Components/src/IComponentActivator.cs diff --git a/src/Components/Components/src/ComponentFactory.cs b/src/Components/Components/src/ComponentFactory.cs index bf5de30d2a..3ba141834b 100644 --- a/src/Components/Components/src/ComponentFactory.cs +++ b/src/Components/Components/src/ComponentFactory.cs @@ -6,6 +6,7 @@ using System.Collections.Concurrent; using System.Linq; using System.Reflection; using Microsoft.AspNetCore.Components.Reflection; +using Microsoft.Extensions.DependencyInjection; namespace Microsoft.AspNetCore.Components { @@ -25,7 +26,12 @@ namespace Microsoft.AspNetCore.Components public IComponent InstantiateComponent(IServiceProvider serviceProvider, Type componentType) { - var instance = Activator.CreateInstance(componentType); + var activator = serviceProvider.GetService(); + + var instance = activator != null + ? activator.CreateInstance(componentType) + : Activator.CreateInstance(componentType); + if (!(instance is IComponent component)) { throw new ArgumentException($"The type {componentType.FullName} does not implement {nameof(IComponent)}.", nameof(componentType)); diff --git a/src/Components/Components/src/DefaultComponentActivator.cs b/src/Components/Components/src/DefaultComponentActivator.cs new file mode 100644 index 0000000000..4f93188f2f --- /dev/null +++ b/src/Components/Components/src/DefaultComponentActivator.cs @@ -0,0 +1,19 @@ +// 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; + +namespace Microsoft.AspNetCore.Components +{ + /// + /// Default implementation of component activator. + /// + public class DefaultComponentActivator : IComponentActivator + { + /// + public IComponent? CreateInstance(Type componentType) + { + return Activator.CreateInstance(componentType) as IComponent; + } + } +} diff --git a/src/Components/Components/src/IComponentActivator.cs b/src/Components/Components/src/IComponentActivator.cs new file mode 100644 index 0000000000..e98e111a05 --- /dev/null +++ b/src/Components/Components/src/IComponentActivator.cs @@ -0,0 +1,20 @@ +// 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; + +namespace Microsoft.AspNetCore.Components +{ + /// + /// Represents an activator that can be used to instantiate components. + /// + public interface IComponentActivator + { + /// + /// Creates an component of the specified type using that type's default constructor. + /// + /// The type of component to create. + /// A reference to the newly created component. + IComponent? CreateInstance(Type componentType); + } +} diff --git a/src/Components/Components/test/ComponentFactoryTest.cs b/src/Components/Components/test/ComponentFactoryTest.cs index 4dd4de56e0..662351bd6f 100644 --- a/src/Components/Components/test/ComponentFactoryTest.cs +++ b/src/Components/Components/test/ComponentFactoryTest.cs @@ -27,6 +27,32 @@ namespace Microsoft.AspNetCore.Components Assert.IsType(instance); } + [Fact] + public void InstantiateComponent_CreatesInstance_WithActivator() + { + // Arrange + var componentType = typeof(EmptyComponent); + var factory = new ComponentFactory(); + + // Act + var instance = factory.InstantiateComponent(GetServiceProviderWithActivator(), componentType); + + // Assert + Assert.NotNull(instance); + Assert.IsType(instance); + } + + [Fact] + public void InstantiateComponent_CreatesInstance_WithActivator_NonComponent() + { + // Arrange + var componentType = typeof(NonComponent); + var factory = new ComponentFactory(); + + // Assert + Assert.Throws(()=>factory.InstantiateComponent(GetServiceProviderWithActivator(), componentType)); + } + [Fact] public void InstantiateComponent_AssignsPropertiesWithInjectAttribute() { @@ -96,6 +122,15 @@ namespace Microsoft.AspNetCore.Components .BuildServiceProvider(); } + private static IServiceProvider GetServiceProviderWithActivator() + { + return new ServiceCollection() + .AddTransient() + .AddTransient() + .AddSingleton() + .BuildServiceProvider(); + } + private class EmptyComponent : IComponent { public void Attach(RenderHandle renderHandle) @@ -162,6 +197,8 @@ namespace Microsoft.AspNetCore.Components public TestService2 Property5 { get; set; } } + private class NonComponent { } + public class TestService1 { } public class TestService2 { } } diff --git a/src/Components/Server/src/DependencyInjection/ComponentServiceCollectionExtensions.cs b/src/Components/Server/src/DependencyInjection/ComponentServiceCollectionExtensions.cs index cd65b7fdc4..b8e66a4e5e 100644 --- a/src/Components/Server/src/DependencyInjection/ComponentServiceCollectionExtensions.cs +++ b/src/Components/Server/src/DependencyInjection/ComponentServiceCollectionExtensions.cs @@ -74,6 +74,7 @@ namespace Microsoft.Extensions.DependencyInjection services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); services.TryAddEnumerable(ServiceDescriptor.Singleton, CircuitOptionsJSInteropDetailedErrorsConfiguration>()); diff --git a/src/Mvc/Mvc.ViewFeatures/src/DependencyInjection/MvcViewFeaturesMvcCoreBuilderExtensions.cs b/src/Mvc/Mvc.ViewFeatures/src/DependencyInjection/MvcViewFeaturesMvcCoreBuilderExtensions.cs index 84ffe50ce1..34b57eb374 100644 --- a/src/Mvc/Mvc.ViewFeatures/src/DependencyInjection/MvcViewFeaturesMvcCoreBuilderExtensions.cs +++ b/src/Mvc/Mvc.ViewFeatures/src/DependencyInjection/MvcViewFeaturesMvcCoreBuilderExtensions.cs @@ -212,12 +212,13 @@ namespace Microsoft.Extensions.DependencyInjection services.TryAddScoped(); services.TryAddScoped(); services.TryAddScoped(); - + services.TryAddTransient(); // This does caching so it should stay singleton services.TryAddSingleton(); services.TryAddSingleton(); + services.TryAddSingleton(); // // Antiforgery