-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Description
Background and Motivation
Currently, Blazor's property injection logic (for [Inject] attributes) is embedded within the internal ComponentFactory class with no public extension point. While IComponentActivator exists for customizing component instantiation, there is no equivalent mechanism for property injection.
This creates a gap for advanced scenarios such as:
- Blazor Hybrid with .NET MAUI: Additional platform-specific context may be needed for property resolution
- Custom DI containers: May need to intercept property injection for custom behavior
- Testing scenarios: Easier mocking and verification of property injection
We cannot extend IComponentActivator to handle property injection as that would be a breaking change for existing custom implementations. Following the pattern established by MVC (which defines separate activators for tag helpers, view components, controllers, and Razor Pages), we propose introducing a separate IComponentPropertyActivator interface specifically for property activation.
Proposed API
namespace Microsoft.AspNetCore.Components;
+ /// <summary>
+ /// Provides a mechanism for activating properties on Blazor component instances.
+ /// </summary>
+ public interface IComponentPropertyActivator
+ {
+ /// <summary>
+ /// Gets a delegate that activates properties on a component of the specified type.
+ /// </summary>
+ /// <param name="componentType">The type of component to create an activator for.</param>
+ /// <returns>
+ /// A delegate that takes an IServiceProvider and an IComponent
+ /// instance, and populates the component's injectable properties.
+ /// </returns>
+ Action<IServiceProvider, IComponent> GetActivator(
+ [DynamicallyAccessedMembers(Component)] Type componentType);
+ }From PublicAPI.Unshipped.txt:
Microsoft.AspNetCore.Components.IComponentPropertyActivator
Microsoft.AspNetCore.Components.IComponentPropertyActivator.GetActivator(System.Type! componentType) -> System.Action<System.IServiceProvider!, Microsoft.AspNetCore.Components.IComponent!>!
Usage Examples
Custom Implementation:
public class CustomPropertyActivator : IComponentPropertyActivator
{
public Action<IServiceProvider, IComponent> GetActivator(Type componentType)
{
// Custom property injection logic with caching
return (serviceProvider, component) =>
{
// Inject properties with custom behavior
// e.g., logging, platform-specific resolution, etc.
};
}
}Registration:
// In Startup.cs or Program.cs
services.AddSingleton<IComponentPropertyActivator, CustomPropertyActivator>();Default Behavior (for reference - this is internal):
The framework provides DefaultComponentPropertyActivator internally which:
- Discovers properties marked with
[Inject] - Supports keyed services via
[Inject(Key = "...")] - Caches activator delegates per component type
- Integrates with Hot Reload for cache invalidation
Alternative Designs
-
Extend
IComponentActivator: We considered adding property injection responsibility to the existingIComponentActivatorinterface. However, this would be a breaking change for existing custom implementations that don't expect to handle property injection. -
Context object parameter: The original issue mentioned considering a context object for future-proofing. We opted for the simpler
IServiceProviderparameter to match the existing Blazor patterns (consistent withIComponentActivator) and avoid unnecessary complexity. If additional context is needed in the future, a new overload or interface version can be introduced. -
MVC's context-based pattern: MVC activators like
ITagHelperActivatoruse context objects (e.g.,ViewContext). We chose the directIServiceProviderapproach to maintain consistency with existing Blazor APIs and because Blazor's rendering context is already available through other means.
Risks
-
Interface extensibility: The interface has a single method. If we need to add more methods in the future, we would need to add default implementations or introduce a new interface version to avoid breaking changes.
-
Performance considerations: Custom implementations should cache activator delegates (as the default implementation does) to avoid repeated reflection costs. Poor implementations could cause performance regressions.
-
No breaking changes identified: This is a purely additive change. The default behavior remains unchanged for applications that don't register a custom
IComponentPropertyActivator.