55using System . Diagnostics . CodeAnalysis ;
66using System . Reflection ;
77using Microsoft . AspNetCore . Components . HotReload ;
8- using Microsoft . AspNetCore . Components . Reflection ;
98using Microsoft . AspNetCore . Components . RenderTree ;
10- using Microsoft . Extensions . DependencyInjection ;
119using static Microsoft . AspNetCore . Internal . LinkerFlags ;
1210
1311namespace Microsoft . AspNetCore . Components ;
@@ -19,10 +17,7 @@ internal sealed class ComponentFactory
1917 AppContext . TryGetSwitch ( "Microsoft.AspNetCore.Components.Unsupported.DisablePropertyInjection" , out var isDisabled ) &&
2018 isDisabled ;
2119
22- private const BindingFlags _injectablePropertyBindingFlags
23- = BindingFlags . Instance | BindingFlags . Public | BindingFlags . NonPublic ;
24-
25- private static readonly ConcurrentDictionary < Type , ComponentTypeInfoCacheEntry > _cachedComponentTypeInfo = new ( ) ;
20+ private static readonly ConcurrentDictionary < Type , IComponentRenderMode ? > _cachedComponentTypeRenderModes = new ( ) ;
2621
2722 static ComponentFactory ( )
2823 {
@@ -33,36 +28,35 @@ static ComponentFactory()
3328 }
3429
3530 private readonly IComponentActivator _componentActivator ;
31+ private readonly IComponentPropertyActivator _propertyActivator ;
3632 private readonly Renderer _renderer ;
3733
38- public ComponentFactory ( IComponentActivator componentActivator , Renderer renderer )
34+ public ComponentFactory ( IComponentActivator componentActivator , IComponentPropertyActivator propertyActivator , Renderer renderer )
3935 {
4036 _componentActivator = componentActivator ?? throw new ArgumentNullException ( nameof ( componentActivator ) ) ;
37+ _propertyActivator = propertyActivator ?? throw new ArgumentNullException ( nameof ( propertyActivator ) ) ;
4138 _renderer = renderer ?? throw new ArgumentNullException ( nameof ( renderer ) ) ;
4239 }
4340
44- public static void ClearCache ( ) => _cachedComponentTypeInfo . Clear ( ) ;
41+ public static void ClearCache ( ) => _cachedComponentTypeRenderModes . Clear ( ) ;
4542
46- private static ComponentTypeInfoCacheEntry GetComponentTypeInfo ( [ DynamicallyAccessedMembers ( Component ) ] Type componentType )
43+ private static IComponentRenderMode ? GetComponentTypeRenderMode ( [ DynamicallyAccessedMembers ( Component ) ] Type componentType )
4744 {
4845 // Unfortunately we can't use 'GetOrAdd' here because the DynamicallyAccessedMembers annotation doesn't flow through to the
4946 // callback, so it becomes an IL2111 warning. The following is equivalent and thread-safe because it's a ConcurrentDictionary
5047 // and it doesn't matter if we build a cache entry more than once.
51- if ( ! _cachedComponentTypeInfo . TryGetValue ( componentType , out var cacheEntry ) )
48+ if ( ! _cachedComponentTypeRenderModes . TryGetValue ( componentType , out var renderMode ) )
5249 {
53- var componentTypeRenderMode = componentType . GetCustomAttribute < RenderModeAttribute > ( ) ? . Mode ;
54- cacheEntry = new ComponentTypeInfoCacheEntry (
55- componentTypeRenderMode ,
56- CreatePropertyInjector ( componentType ) ) ;
57- _cachedComponentTypeInfo . TryAdd ( componentType , cacheEntry ) ;
50+ renderMode = componentType . GetCustomAttribute < RenderModeAttribute > ( ) ? . Mode ;
51+ _cachedComponentTypeRenderModes . TryAdd ( componentType , renderMode ) ;
5852 }
5953
60- return cacheEntry ;
54+ return renderMode ;
6155 }
6256
6357 public IComponent InstantiateComponent ( IServiceProvider serviceProvider , [ DynamicallyAccessedMembers ( Component ) ] Type componentType , IComponentRenderMode ? callerSpecifiedRenderMode , int ? parentComponentId )
6458 {
65- var ( componentTypeRenderMode , propertyInjector ) = GetComponentTypeInfo ( componentType ) ;
59+ var componentTypeRenderMode = GetComponentTypeRenderMode ( componentType ) ;
6660 IComponent component ;
6761
6862 if ( componentTypeRenderMode is null && callerSpecifiedRenderMode is null )
@@ -89,111 +83,19 @@ public IComponent InstantiateComponent(IServiceProvider serviceProvider, [Dynami
8983
9084 if ( ! _propertyInjectionDisabled )
9185 {
92- if ( component . GetType ( ) == componentType )
93- {
94- // Fast, common case: use the cached data we already looked up
95- propertyInjector ( serviceProvider , component ) ;
96- }
97- else
98- {
99- // Uncommon case where the activator/resolver returned a different type. Needs an extra cache lookup.
100- PerformPropertyInjection ( serviceProvider , component ) ;
101- }
86+ PerformPropertyInjection ( serviceProvider , component ) ;
10287 }
10388
10489 return component ;
10590 }
10691
107- private static void PerformPropertyInjection ( IServiceProvider serviceProvider , IComponent instance )
92+ private void PerformPropertyInjection ( IServiceProvider serviceProvider , IComponent instance )
10893 {
10994 // Suppressed with "pragma warning disable" so ILLink Roslyn Anayzer doesn't report the warning.
110- #pragma warning disable IL2072 // 'componentType' argument does not satisfy 'DynamicallyAccessedMemberTypes.All' in call to 'Microsoft.AspNetCore.Components.ComponentFactory.GetComponentTypeInfo(Type)'.
111- var componentTypeInfo = GetComponentTypeInfo ( instance . GetType ( ) ) ;
112- #pragma warning restore IL2072 // 'componentType' argument does not satisfy 'DynamicallyAccessedMemberTypes.All' in call to 'Microsoft.AspNetCore.Components.ComponentFactory.GetComponentTypeInfo(Type)'.
113-
114- componentTypeInfo . PerformPropertyInjection ( serviceProvider , instance ) ;
115- }
116-
117- private static Action < IServiceProvider , IComponent > CreatePropertyInjector ( [ DynamicallyAccessedMembers ( Component ) ] Type type )
118- {
119- // Do all the reflection up front
120- List < ( string name , Type propertyType , PropertySetter setter , object ? serviceKey ) > ? injectables = null ;
121- foreach ( var property in MemberAssignment . GetPropertiesIncludingInherited ( type , _injectablePropertyBindingFlags ) )
122- {
123- var injectAttribute = property . GetCustomAttribute < InjectAttribute > ( ) ;
124- if ( injectAttribute is null )
125- {
126- continue ;
127- }
128-
129- injectables ??= new ( ) ;
130- injectables . Add ( ( property . Name , property . PropertyType , new PropertySetter ( type , property ) , injectAttribute . Key ) ) ;
131- }
132-
133- if ( injectables is null )
134- {
135- return static ( _ , _ ) => { } ;
136- }
137-
138- return Initialize ;
95+ #pragma warning disable IL2072 // 'componentType' argument does not satisfy 'DynamicallyAccessedMemberTypes.All' in call to 'IComponentPropertyActivator.GetActivator(Type)'.
96+ var propertyActivator = _propertyActivator . GetActivator ( instance . GetType ( ) ) ;
97+ #pragma warning restore IL2072
13998
140- // Return an action whose closure can write all the injected properties
141- // without any further reflection calls (just typecasts)
142- void Initialize ( IServiceProvider serviceProvider , IComponent component )
143- {
144- foreach ( var ( propertyName , propertyType , setter , serviceKey ) in injectables )
145- {
146- object ? serviceInstance ;
147-
148- if ( serviceKey is not null )
149- {
150- if ( serviceProvider is not IKeyedServiceProvider keyedServiceProvider )
151- {
152- throw new InvalidOperationException ( $ "Cannot provide a value for property " +
153- $ "'{ propertyName } ' on type '{ type . FullName } '. The service provider " +
154- $ "does not implement '{ nameof ( IKeyedServiceProvider ) } ' and therefore " +
155- $ "cannot provide keyed services.") ;
156- }
157-
158- serviceInstance = keyedServiceProvider . GetKeyedService ( propertyType , serviceKey )
159- ?? throw new InvalidOperationException ( $ "Cannot provide a value for property " +
160- $ "'{ propertyName } ' on type '{ type . FullName } '. There is no " +
161- $ "registered keyed service of type '{ propertyType } ' with key '{ serviceKey } '.") ;
162- }
163- else
164- {
165- serviceInstance = serviceProvider . GetService ( propertyType )
166- ?? throw new InvalidOperationException ( $ "Cannot provide a value for property " +
167- $ "'{ propertyName } ' on type '{ type . FullName } '. There is no " +
168- $ "registered service of type '{ propertyType } '.") ;
169- }
170-
171- setter . SetValue ( component , serviceInstance ) ;
172- }
173- }
174- }
175-
176- // Tracks information about a specific component type that ComponentFactory uses
177- private sealed class ComponentTypeInfoCacheEntry
178- {
179- public IComponentRenderMode ? ComponentTypeRenderMode { get ; }
180-
181- public Action < IServiceProvider , IComponent > PerformPropertyInjection { get ; }
182-
183- public ComponentTypeInfoCacheEntry (
184- IComponentRenderMode ? componentTypeRenderMode ,
185- Action < IServiceProvider , IComponent > performPropertyInjection )
186- {
187- ComponentTypeRenderMode = componentTypeRenderMode ;
188- PerformPropertyInjection = performPropertyInjection ;
189- }
190-
191- public void Deconstruct (
192- out IComponentRenderMode ? componentTypeRenderMode ,
193- out Action < IServiceProvider , IComponent > performPropertyInjection )
194- {
195- componentTypeRenderMode = ComponentTypeRenderMode ;
196- performPropertyInjection = PerformPropertyInjection ;
197- }
99+ propertyActivator ( serviceProvider , instance ) ;
198100 }
199101}
0 commit comments