You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
[Blazor] DisplayName/Label: ExpressionMemberAccessor caches every accessor expression instance (memory leak on Blazor Server) #70031
ExpressionMemberAccessor._memberInfoCache is a static ConcurrentDictionary<Expression, MemberInfo>. Expression uses reference equality, and a lambda such as () => Model.Code builds a new expression tree on every evaluation, so every parent render of <DisplayName For="..."> or <Label For="..."> adds a new entry. Each entry keeps the expression alive, and with it the captured closure / component instance and everything it references. On Blazor Server this grows for the lifetime of the process. InputNumber / InputDate hit the same path on every parsing error (#68667).
The _displayNameCache keyed by (MemberInfo, culture) is fine; only the expression-keyed cache is the problem.
Expected Behavior
The cache size is bounded by the number of distinct members, e.g. resolve the MemberInfo from the expression without caching by expression instance.
Steps To Reproduce
net11.0 console app with <FrameworkReference Include="Microsoft.AspNetCore.App" /> (reflection only reads the internal cache size):
usingSystem.Collections;usingSystem.ComponentModel;usingSystem.Linq.Expressions;usingSystem.Reflection;vartype=typeof(Microsoft.AspNetCore.Components.Forms.DisplayName<>).Assembly.GetType("Microsoft.AspNetCore.Components.Forms.ExpressionMemberAccessor",true)!;varget=type.GetMethods(BindingFlags.Public|BindingFlags.Static).First(m =>m.Name=="GetDisplayName"&&m.IsGenericMethod).MakeGenericMethod(typeof(string));varcache=(ICollection)type.GetField("_memberInfoCache",BindingFlags.NonPublic|BindingFlags.Static)!.GetValue(null)!;varpage=newPage();for(vari=0;i<1000;i++){Expression<Func<string>>accessor=()=>page.Model.Code;// new instance, like every renderget.Invoke(null,[accessor]);}Console.WriteLine(cache.Count);// prints 1000classPage{publicModelModel{get;}=new();}classModel{[DisplayName("Code")]publicstringCode{get;set;}="";}
The same happens with <DisplayName For="@(() => Model.Code)" /> inside a form that re-renders on every field change.
Activity
github-actions commentedon Oct 24, 2026
Triage Summary
Area:
area-blazor(ExpressionMemberAccessorinsrc/Components/Web/src/Forms, used byDisplayName/Label/InputNumber/InputDate)Type:
Bug(preserved)Potential Duplicates