Skip to content

Commit 39da5b1

Browse files
Clarify RenderModeAttribute inheritance. Fixes #49848
1 parent 91f14fc commit 39da5b1

File tree

2 files changed

+34
-1
lines changed

2 files changed

+34
-1
lines changed

src/Components/Components/src/RenderModeAttribute.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ namespace Microsoft.AspNetCore.Components;
1010
/// be implemented to work across all render modes. Component authors should only specify
1111
/// a fixed rendering mode when the component is incapable of running in other modes.
1212
/// </summary>
13-
[AttributeUsage(AttributeTargets.Class)]
13+
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)]
1414
public abstract class RenderModeAttribute : Attribute
1515
{
1616
/// <summary>

src/Components/Components/test/ComponentFactoryTest.cs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -151,6 +151,29 @@ public void InstantiateComponent_WithRenderModeOnComponent_UsesRenderModeResolve
151151
Assert.IsType<TestRenderMode>(renderer.SuppliedRenderMode);
152152
}
153153

154+
[Fact]
155+
public void InstantiateComponent_WithDerivedRenderModeOnDerivedComponent_CausesAmbiguousMatchException()
156+
{
157+
// We could allow derived components to override the rendermode, but:
158+
// [1] It's unclear how that would be legitimate. If the base specifies a rendermode, it's saying
159+
// it only works in that mode. It wouldn't be safe for a derived type to change that.
160+
// [2] If we did want to implement this, we'd need to implement our own inheritance chain walking
161+
// to make sure we find the rendermode from the *closest* ancestor type. GetCustomAttributes
162+
// on its own isn't documented to return the results in any specific order.
163+
// Since issue [1] makes it unclear we'd want to support this, for now we don't.
164+
165+
// Arrange
166+
var resolvedComponent = new ComponentWithInjectProperties();
167+
var componentType = typeof(DerivedComponentWithRenderMode);
168+
var renderer = new RendererWithResolveComponentForRenderMode(resolvedComponent);
169+
var componentActivator = new DefaultComponentActivator();
170+
var factory = new ComponentFactory(componentActivator, renderer);
171+
172+
// Act/Assert
173+
Assert.Throws<AmbiguousMatchException>(
174+
() => factory.InstantiateComponent(GetServiceProvider(), componentType, null, 1234));
175+
}
176+
154177
[Fact]
155178
public void InstantiateComponent_WithRenderModeOnCallSite_UsesRenderModeResolver()
156179
{
@@ -290,6 +313,16 @@ public IComponent CreateInstance(Type componentType)
290313
}
291314

292315
private class TestRenderMode : IComponentRenderMode { }
316+
private class DerivedComponentRenderMode : IComponentRenderMode { }
317+
318+
[DerivedComponentRenderMode]
319+
private class DerivedComponentWithRenderMode : ComponentWithRenderMode
320+
{
321+
class DerivedComponentRenderModeAttribute : RenderModeAttribute
322+
{
323+
public override IComponentRenderMode Mode => new DerivedComponentRenderMode();
324+
}
325+
}
293326

294327
[OwnRenderMode]
295328
private class ComponentWithRenderMode : IComponent

0 commit comments

Comments
 (0)