Skip to content

Commit 87f870d

Browse files
authored
Revert "Activate components by default" (#40526)
Reverting type activation based on discussion in #40521 This reverts commit 197d527. This reverts commit 0e7409e.
1 parent e9dddd6 commit 87f870d

9 files changed

+71
-128
lines changed

src/Components/Components/src/ComponentFactory.cs

+18-69
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
using System.Diagnostics.CodeAnalysis;
66
using System.Reflection;
77
using Microsoft.AspNetCore.Components.Reflection;
8-
using Microsoft.Extensions.DependencyInjection;
98
using static Microsoft.AspNetCore.Internal.LinkerFlags;
109

1110
namespace Microsoft.AspNetCore.Components;
@@ -15,71 +14,46 @@ internal sealed class ComponentFactory
1514
private const BindingFlags _injectablePropertyBindingFlags
1615
= BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
1716

18-
private static readonly ConcurrentDictionary<Type, ComponentInitializer> _cachedInitializers = new();
19-
private readonly IComponentActivator? _componentActivator;
17+
private static readonly ConcurrentDictionary<Type, Action<IServiceProvider, IComponent>> _cachedInitializers = new();
2018

21-
public ComponentFactory(IComponentActivator? componentActivator)
19+
private readonly IComponentActivator _componentActivator;
20+
21+
public ComponentFactory(IComponentActivator componentActivator)
2222
{
23-
_componentActivator = componentActivator;
23+
_componentActivator = componentActivator ?? throw new ArgumentNullException(nameof(componentActivator));
2424
}
2525

2626
public static void ClearCache() => _cachedInitializers.Clear();
2727

2828
public IComponent InstantiateComponent(IServiceProvider serviceProvider, [DynamicallyAccessedMembers(Component)] Type componentType)
2929
{
30-
if (_componentActivator is not null)
31-
{
32-
return InstantiateWithActivator(_componentActivator, serviceProvider, componentType);
33-
}
34-
35-
return InstantiateDefault(serviceProvider, componentType);
36-
}
37-
38-
private static IComponent InstantiateDefault(IServiceProvider serviceProvider, [DynamicallyAccessedMembers(Component)] Type componentType)
39-
{
40-
// This is thread-safe because _cachedInitializers is a ConcurrentDictionary.
41-
// We might generate the initializer more than once for a given type, but would
42-
// still produce the correct result.
43-
if (!_cachedInitializers.TryGetValue(componentType, out var initializer))
44-
{
45-
if (!typeof(IComponent).IsAssignableFrom(componentType))
46-
{
47-
throw new ArgumentException($"The type {componentType.FullName} does not implement {nameof(IComponent)}.", nameof(componentType));
48-
}
49-
50-
initializer = new(CreatePropertyInitializer(componentType), ActivatorUtilities.CreateFactory(componentType, Type.EmptyTypes));
51-
_cachedInitializers.TryAdd(componentType, initializer);
52-
}
53-
54-
return initializer.CreateDefault(serviceProvider);
55-
}
56-
57-
private static IComponent InstantiateWithActivator(IComponentActivator componentActivator, IServiceProvider serviceProvider, [DynamicallyAccessedMembers(Component)] Type componentType)
58-
{
59-
var component = componentActivator.CreateInstance(componentType);
30+
var component = _componentActivator.CreateInstance(componentType);
6031
if (component is null)
6132
{
62-
// A user implemented IComponentActivator might return null.
33+
// The default activator will never do this, but an externally-supplied one might
6334
throw new InvalidOperationException($"The component activator returned a null value for a component of type {componentType.FullName}.");
6435
}
6536

66-
// Use the activated type instead of specified type since the activator may return different/ derived instances.
67-
componentType = component.GetType();
37+
PerformPropertyInjection(serviceProvider, component);
38+
return component;
39+
}
6840

41+
private static void PerformPropertyInjection(IServiceProvider serviceProvider, IComponent instance)
42+
{
6943
// This is thread-safe because _cachedInitializers is a ConcurrentDictionary.
7044
// We might generate the initializer more than once for a given type, but would
7145
// still produce the correct result.
72-
if (!_cachedInitializers.TryGetValue(componentType, out var initializer))
46+
var instanceType = instance.GetType();
47+
if (!_cachedInitializers.TryGetValue(instanceType, out var initializer))
7348
{
74-
initializer = new(CreatePropertyInitializer(componentType));
75-
_cachedInitializers.TryAdd(componentType, initializer);
49+
initializer = CreateInitializer(instanceType);
50+
_cachedInitializers.TryAdd(instanceType, initializer);
7651
}
7752

78-
initializer.ActivateProperties(serviceProvider, component);
79-
return component;
53+
initializer(serviceProvider, instance);
8054
}
8155

82-
private static Action<IServiceProvider, IComponent> CreatePropertyInitializer([DynamicallyAccessedMembers(Component)] Type type)
56+
private static Action<IServiceProvider, IComponent> CreateInitializer([DynamicallyAccessedMembers(Component)] Type type)
8357
{
8458
// Do all the reflection up front
8559
List<(string name, Type propertyType, PropertySetter setter)>? injectables = null;
@@ -119,29 +93,4 @@ void Initialize(IServiceProvider serviceProvider, IComponent component)
11993
}
12094
}
12195
}
122-
123-
private readonly struct ComponentInitializer
124-
{
125-
private readonly Action<IServiceProvider, IComponent> _propertyInitializer;
126-
127-
private readonly ObjectFactory? _componentFactory;
128-
129-
public ComponentInitializer(Action<IServiceProvider, IComponent> propertyInitializer, ObjectFactory? componentFactory = null)
130-
{
131-
_propertyInitializer = propertyInitializer;
132-
_componentFactory = componentFactory;
133-
}
134-
135-
public IComponent CreateDefault(IServiceProvider serviceProvider)
136-
{
137-
var component = (IComponent)_componentFactory!(serviceProvider, Array.Empty<object?>());
138-
ActivateProperties(serviceProvider, component);
139-
return component;
140-
}
141-
142-
public void ActivateProperties(IServiceProvider serviceProvider, IComponent component)
143-
{
144-
_propertyInitializer(serviceProvider, component);
145-
}
146-
}
14796
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
using System.Diagnostics.CodeAnalysis;
5+
6+
namespace Microsoft.AspNetCore.Components;
7+
8+
internal class DefaultComponentActivator : IComponentActivator
9+
{
10+
public static IComponentActivator Instance { get; } = new DefaultComponentActivator();
11+
12+
/// <inheritdoc />
13+
public IComponent CreateInstance([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] Type componentType)
14+
{
15+
if (!typeof(IComponent).IsAssignableFrom(componentType))
16+
{
17+
throw new ArgumentException($"The type {componentType.FullName} does not implement {nameof(IComponent)}.", nameof(componentType));
18+
}
19+
20+
return (IComponent)Activator.CreateInstance(componentType)!;
21+
}
22+
}

src/Components/Components/src/Microsoft.AspNetCore.Components.WarningSuppressions.xml

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<linker>
3-
<assembly fullname="Microsoft.AspNetCore.Components, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60">
3+
<assembly fullname="Microsoft.AspNetCore.Components, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60">
44
<attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
55
<argument>ILLink</argument>
66
<argument>IL2026</argument>
@@ -41,7 +41,7 @@
4141
<argument>ILLink</argument>
4242
<argument>IL2072</argument>
4343
<property name="Scope">member</property>
44-
<property name="Target">M:Microsoft.AspNetCore.Components.ComponentFactory.InstantiateWithActivator(Microsoft.AspNetCore.Components.IComponentActivator,System.IServiceProvider,System.Type)</property>
44+
<property name="Target">M:Microsoft.AspNetCore.Components.ComponentFactory.PerformPropertyInjection(System.IServiceProvider,Microsoft.AspNetCore.Components.IComponent)</property>
4545
</attribute>
4646
<attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
4747
<argument>ILLink</argument>
@@ -59,7 +59,7 @@
5959
<argument>ILLink</argument>
6060
<argument>IL2077</argument>
6161
<property name="Scope">member</property>
62-
<property name="Target">M:Microsoft.AspNetCore.Components.ComponentFactory.CreatePropertyInitializer(System.Type)</property>
62+
<property name="Target">M:Microsoft.AspNetCore.Components.ComponentFactory.CreateInitializer(System.Type)</property>
6363
</attribute>
6464
<attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
6565
<argument>ILLink</argument>

src/Components/Components/src/RenderTree/Renderer.cs

+8-5
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ public event UnhandledExceptionEventHandler UnhandledSynchronizationException
6464
/// <param name="serviceProvider">The <see cref="IServiceProvider"/> to be used when initializing components.</param>
6565
/// <param name="loggerFactory">The <see cref="ILoggerFactory"/>.</param>
6666
public Renderer(IServiceProvider serviceProvider, ILoggerFactory loggerFactory)
67-
: this(serviceProvider, loggerFactory, GetComponentActivatorOrDefault(serviceProvider)!)
67+
: this(serviceProvider, loggerFactory, GetComponentActivatorOrDefault(serviceProvider))
6868
{
6969
// This overload is provided for back-compatibility
7070
}
@@ -87,8 +87,10 @@ public Renderer(IServiceProvider serviceProvider, ILoggerFactory loggerFactory,
8787
throw new ArgumentNullException(nameof(loggerFactory));
8888
}
8989

90-
// Even though IComponentActivator is not marked as nullable, we do allow null because that's how the framework internally indicates
91-
// that we should use default activation logic.
90+
if (componentActivator is null)
91+
{
92+
throw new ArgumentNullException(nameof(componentActivator));
93+
}
9294

9395
_serviceProvider = serviceProvider;
9496
_logger = loggerFactory.CreateLogger<Renderer>();
@@ -97,9 +99,10 @@ public Renderer(IServiceProvider serviceProvider, ILoggerFactory loggerFactory,
9799

98100
internal HotReloadManager HotReloadManager { get; set; } = HotReloadManager.Default;
99101

100-
private static IComponentActivator? GetComponentActivatorOrDefault(IServiceProvider serviceProvider)
102+
private static IComponentActivator GetComponentActivatorOrDefault(IServiceProvider serviceProvider)
101103
{
102-
return serviceProvider.GetService<IComponentActivator>();
104+
return serviceProvider.GetService<IComponentActivator>()
105+
?? DefaultComponentActivator.Instance;
103106
}
104107

105108
/// <summary>

src/Components/Components/test/ComponentFactoryTest.cs

+3-46
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ public void InstantiateComponent_CreatesInstance()
1212
{
1313
// Arrange
1414
var componentType = typeof(EmptyComponent);
15-
var factory = new ComponentFactory(null);
15+
var factory = new ComponentFactory(new DefaultComponentActivator());
1616

1717
// Act
1818
var instance = factory.InstantiateComponent(GetServiceProvider(), componentType);
@@ -22,30 +22,12 @@ public void InstantiateComponent_CreatesInstance()
2222
Assert.IsType<EmptyComponent>(instance);
2323
}
2424

25-
[Fact]
26-
public void InstantiateComponent_CreatesInstance_WithTypeActivation()
27-
{
28-
// Arrange
29-
var componentType = typeof(ComponentWithConstructorInjection);
30-
var factory = new ComponentFactory(null);
31-
32-
// Act
33-
var instance = factory.InstantiateComponent(GetServiceProvider(), componentType);
34-
35-
// Assert
36-
Assert.NotNull(instance);
37-
var component = Assert.IsType<ComponentWithConstructorInjection>(instance);
38-
Assert.NotNull(component.Property1);
39-
Assert.NotNull(component.Property2);
40-
Assert.NotNull(component.Property3); // Property injection should still work.
41-
}
42-
4325
[Fact]
4426
public void InstantiateComponent_CreatesInstance_NonComponent()
4527
{
4628
// Arrange
4729
var componentType = typeof(List<string>);
48-
var factory = new ComponentFactory(null);
30+
var factory = new ComponentFactory(new DefaultComponentActivator());
4931

5032
// Assert
5133
var ex = Assert.Throws<ArgumentException>(() => factory.InstantiateComponent(GetServiceProvider(), componentType));
@@ -113,7 +95,7 @@ public void InstantiateComponent_IgnoresPropertiesWithoutInjectAttribute()
11395
{
11496
// Arrange
11597
var componentType = typeof(ComponentWithNonInjectableProperties);
116-
var factory = new ComponentFactory(null);
98+
var factory = new ComponentFactory(new DefaultComponentActivator());
11799

118100
// Act
119101
var instance = factory.InstantiateComponent(GetServiceProvider(), componentType);
@@ -147,31 +129,6 @@ public Task SetParametersAsync(ParameterView parameters)
147129
}
148130
}
149131

150-
public class ComponentWithConstructorInjection : IComponent
151-
{
152-
public ComponentWithConstructorInjection(TestService1 property1, TestService2 property2)
153-
{
154-
Property1 = property1;
155-
Property2 = property2;
156-
}
157-
158-
public TestService1 Property1 { get; }
159-
public TestService2 Property2 { get; }
160-
161-
[Inject]
162-
public TestService2 Property3 { get; set; }
163-
164-
public void Attach(RenderHandle renderHandle)
165-
{
166-
throw new NotImplementedException();
167-
}
168-
169-
public Task SetParametersAsync(ParameterView parameters)
170-
{
171-
throw new NotImplementedException();
172-
}
173-
}
174-
175132
private class ComponentWithInjectProperties : IComponent
176133
{
177134
[Inject]

src/Components/Forms/src/Microsoft.AspNetCore.Components.Forms.WarningSuppressions.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<linker>
3-
<assembly fullname="Microsoft.AspNetCore.Components.Forms, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60">
3+
<assembly fullname="Microsoft.AspNetCore.Components.Forms, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60">
44
<attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
55
<argument>ILLink</argument>
66
<argument>IL2026</argument>

src/Components/Web/src/Microsoft.AspNetCore.Components.Web.WarningSuppressions.xml

+13-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,18 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<linker>
3-
<assembly fullname="Microsoft.AspNetCore.Components.Web, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60">
3+
<assembly fullname="Microsoft.AspNetCore.Components.Web, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60">
4+
<attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
5+
<argument>ILLink</argument>
6+
<argument>IL2026</argument>
7+
<property name="Scope">member</property>
8+
<property name="Target">M:Microsoft.AspNetCore.Components.Web.Infrastructure.JSComponentInterop.SetRootComponentParameters(System.Int32,System.Int32,System.Text.Json.JsonElement,System.Text.Json.JsonSerializerOptions)</property>
9+
</attribute>
10+
<attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
11+
<argument>ILLink</argument>
12+
<argument>IL2026</argument>
13+
<property name="Scope">member</property>
14+
<property name="Target">M:Microsoft.AspNetCore.Components.Web.WebEventData.ParseEventArgsJson(Microsoft.AspNetCore.Components.RenderTree.Renderer,System.Text.Json.JsonSerializerOptions,System.UInt64,System.String,System.Text.Json.JsonElement)</property>
15+
</attribute>
416
<attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
517
<argument>ILLink</argument>
618
<argument>IL2062</argument>

src/Components/WebAssembly/WebAssembly.Authentication/src/Microsoft.AspNetCore.Components.WebAssembly.Authentication.WarningSuppressions.xml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<?xml version="1.0" encoding="utf-8"?>
1+
<?xml version="1.0" encoding="utf-8"?>
22
<linker>
33
<assembly fullname="Microsoft.AspNetCore.Components.WebAssembly.Authentication, Version=42.42.42.42, Culture=neutral, PublicKeyToken=adb9793829ddae60">
44
<attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
@@ -14,4 +14,4 @@
1414
<property name="Target">M:Microsoft.Extensions.DependencyInjection.WebAssemblyAuthenticationServiceCollectionExtensions.&lt;&gt;c__1`1.&lt;AddAuthenticationStateProvider&gt;b__1_0(System.IServiceProvider)</property>
1515
</attribute>
1616
</assembly>
17-
</linker>
17+
</linker>

src/JSInterop/Microsoft.JSInterop/src/Microsoft.JSInterop.WarningSuppressions.xml

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<linker>
3-
<assembly fullname="Microsoft.JSInterop, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60">
3+
<assembly fullname="Microsoft.JSInterop, Version=6.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60">
44
<attribute fullname="System.Diagnostics.CodeAnalysis.UnconditionalSuppressMessageAttribute">
55
<argument>ILLink</argument>
66
<argument>IL2026</argument>

0 commit comments

Comments
 (0)