dotnet / dotnet/aspnetcore

Add Model + FieldName overload for InputBase and ValidationMessage to avoid expression parsing

Open
#61,711 1 comment 3 reactions 0 assignees View on GitHub
area-blazor
Dominant language
C#
Stars
38.4k
Forks
10.9k
Avg merge
2d 6h
Merged PRs (30d)
290

Description

### Is there an existing issue for this?

- [x] I have searched the existing issues

### Is your feature request related to a problem? Please describe the problem.

Currently, Blazor’s InputBase and ValidationMessage components require a ValueExpression (e.g., @(() => Model.Property)) to create a FieldIdentifier.

While very convenient and expressive for developers, this design has measurable runtime costs:

- Expression parsing is relatively slow, especially when done for many components at once (e.g., large or dynamic forms).
- Memory allocations for closure objects, captured variables, and parsed trees add additional pressure.
- On WebAssembly (WASM), where CPU/memory is limited, this cost is even more visible.

Even the internal FieldIdentifier.cs file acknowledges the cost concern:

// It would be great to cache this somehow, but it's unclear there's a reasonable way to do
// so, given that it embeds captured values such as "this". We could consider special-casing
// for "() => something.Member" and building a cache keyed by "something.GetType()" with values
// of type Func so we can cheaply map from "something" to "something.Member".

This shows that the Blazor team itself recognized that expression parsing is non-trivial and expensive.

However, caching strategies are difficult because of captured closure values, instance-specific references (this), and type safety.

### Describe the solution you'd like

Introduce optional parameters for "Model" and "FieldName" on InputBase and ValidationMessage components.

If developers opt-in, Blazor can completely skip parsing the ValueExpression and build a FieldIdentifier directly.

Example:

```

```

Behind the scenes, FieldIdentifier can be created like:

`new FieldIdentifier(model, fieldName)`

No parsing.
No closures.
No slow reflection.

I would have created my own components inheriting from InputBase but I had to provide a dummy ValueExpression so that SetParametersAsync doesn't throw an Exception. Even if I precompute the FieldIdentifier in OnInitialized, I cannot avoid the parsing of ValueExpression in SetParametersAsync.

### Additional context

Why this makes sense

- Backward compatible: Existing @(() => Model.Property) usage remains unchanged.
- Opt-in optimization: Only those who want max performance need to use Model+FieldName.
- Zero runtime cost: Initialization of FieldIdentifier happens once during OnInitialized().
- Helps dynamic UI: Forms generated dynamically would benefit enormously.
- WASM-friendly: Faster rendering on Blazor WebAssembly scenarios.
- Simpler for large enterprise forms.

I estimate that bypassing expression parsing and directly accepting Model and FieldName in very large forms (100+ inputs) will improve considerably the user experience on page load while reducing CPU utilization.
Action Estimated cost per field
Parsing expression tree ~1.5 - 2 ms
Creating FieldIdentifier manually < 0.01 ms
Accessing property via instance < 0.001 ms

The risks are very low:

- No breaking changes: all new functionality would be opt-in.
- Advanced developers would choose the more verbose but faster path only if they need it.

**Conclusion**

Adding Model + FieldName overloads for Blazor input components is a small and safe improvement with big performance wins, especially for large or dynamic forms.

It aligns with the optimization hints already considered in the internal source code comments.

🙏 Thanks for considering this optimization!

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.