Skip to content

[API Proposal] IComponentPropertyActivator for Blazor property injection #64596

@javiercn

Description

@javiercn

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

  1. Extend IComponentActivator: We considered adding property injection responsibility to the existing IComponentActivator interface. However, this would be a breaking change for existing custom implementations that don't expect to handle property injection.

  2. Context object parameter: The original issue mentioned considering a context object for future-proofing. We opted for the simpler IServiceProvider parameter to match the existing Blazor patterns (consistent with IComponentActivator) and avoid unnecessary complexity. If additional context is needed in the future, a new overload or interface version can be introduced.

  3. MVC's context-based pattern: MVC activators like ITagHelperActivator use context objects (e.g., ViewContext). We chose the direct IServiceProvider approach to maintain consistency with existing Blazor APIs and because Blazor's rendering context is already available through other means.

Risks

  1. 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.

  2. Performance considerations: Custom implementations should cache activator delegates (as the default implementation does) to avoid repeated reflection costs. Poor implementations could cause performance regressions.

  3. No breaking changes identified: This is a purely additive change. The default behavior remains unchanged for applications that don't register a custom IComponentPropertyActivator.


Implementation: #64595
Related Issue: #63451

Metadata

Metadata

Assignees

No one assigned

    Labels

    api-ready-for-reviewAPI is ready for formal API review - https://github.com/dotnet/apireviewsarea-blazorIncludes: Blazor, Razor Components

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions