Blazor supports DisplayName for models#14
Conversation
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Javier Calvarro Nelson <jacalvar@microsoft.com>
|
|
||
| internal static class ExpressionMemberAccessor | ||
| { | ||
| private static readonly ConcurrentDictionary<Expression, MemberInfo> _memberInfoCache = new(); |
There was a problem hiding this comment.
🔴 Memory leak due to ineffective Expression-keyed cache using reference equality
The _memberInfoCache in ExpressionMemberAccessor uses Expression as a dictionary key, but Expression objects use reference equality by default. Since lambda expressions like () => model.Property create new Expression objects each time a component renders, the cache will never hit and will grow unbounded.
Click to expand
How the bug is triggered
Every time a DisplayName component re-renders (or a parent component re-renders), a new lambda expression object is created for the For parameter. For example:
<DisplayName For="@(() => _product.Name)" />This creates a new Expression<Func<TValue>> instance on each render. In ExpressionMemberAccessor.cs:30:
return _memberInfoCache.GetOrAdd(accessor, static expr => { ... });Since ConcurrentDictionary uses reference equality for Expression keys, the cache lookup never matches existing entries.
Actual vs Expected Behavior
- Actual: Cache grows unbounded as new expression objects are added but never retrieved. Each render adds a new entry.
- Expected: Cache should use structural equality (like
LambdaExpressionComparerused insrc/Mvc/Mvc.ViewFeatures/src/ModelExpressionProvider.cs:27) to match equivalent expressions.
Impact
Memory leak in long-running Blazor applications using the DisplayName component, as the static cache will accumulate entries indefinitely.
Recommendation: Either use a custom IEqualityComparer<Expression> similar to LambdaExpressionComparer from MVC, or cache by MemberInfo directly (which does support equality properly) instead of by Expression. Alternatively, remove the ineffective cache entirely.
Was this helpful? React with 👍 or 👎 to provide feedback.
Benchmark PR from qodo-benchmark#84