From a71c70d02c0cb29117f46e9e10b4ac5fb4d45f52 Mon Sep 17 00:00:00 2001 From: Steve Sanderson Date: Mon, 21 Aug 2023 12:03:22 +0100 Subject: [PATCH] Clarify RenderModeAttribute inheritance. Fixes #49848 --- .../Components/src/RenderModeAttribute.cs | 2 +- .../Components/test/ComponentFactoryTest.cs | 33 +++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/src/Components/Components/src/RenderModeAttribute.cs b/src/Components/Components/src/RenderModeAttribute.cs index d9d7ff286270..87fb58c0e9de 100644 --- a/src/Components/Components/src/RenderModeAttribute.cs +++ b/src/Components/Components/src/RenderModeAttribute.cs @@ -10,7 +10,7 @@ namespace Microsoft.AspNetCore.Components; /// be implemented to work across all render modes. Component authors should only specify /// a fixed rendering mode when the component is incapable of running in other modes. /// -[AttributeUsage(AttributeTargets.Class)] +[AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)] public abstract class RenderModeAttribute : Attribute { /// diff --git a/src/Components/Components/test/ComponentFactoryTest.cs b/src/Components/Components/test/ComponentFactoryTest.cs index 7eca91d4471a..fd18139905bf 100644 --- a/src/Components/Components/test/ComponentFactoryTest.cs +++ b/src/Components/Components/test/ComponentFactoryTest.cs @@ -151,6 +151,29 @@ public void InstantiateComponent_WithRenderModeOnComponent_UsesRenderModeResolve Assert.IsType(renderer.SuppliedRenderMode); } + [Fact] + public void InstantiateComponent_WithDerivedRenderModeOnDerivedComponent_CausesAmbiguousMatchException() + { + // We could allow derived components to override the rendermode, but: + // [1] It's unclear how that would be legitimate. If the base specifies a rendermode, it's saying + // it only works in that mode. It wouldn't be safe for a derived type to change that. + // [2] If we did want to implement this, we'd need to implement our own inheritance chain walking + // to make sure we find the rendermode from the *closest* ancestor type. GetCustomAttributes + // on its own isn't documented to return the results in any specific order. + // Since issue [1] makes it unclear we'd want to support this, for now we don't. + + // Arrange + var resolvedComponent = new ComponentWithInjectProperties(); + var componentType = typeof(DerivedComponentWithRenderMode); + var renderer = new RendererWithResolveComponentForRenderMode(resolvedComponent); + var componentActivator = new DefaultComponentActivator(); + var factory = new ComponentFactory(componentActivator, renderer); + + // Act/Assert + Assert.Throws( + () => factory.InstantiateComponent(GetServiceProvider(), componentType, null, 1234)); + } + [Fact] public void InstantiateComponent_WithRenderModeOnCallSite_UsesRenderModeResolver() { @@ -290,6 +313,16 @@ public IComponent CreateInstance(Type componentType) } private class TestRenderMode : IComponentRenderMode { } + private class DerivedComponentRenderMode : IComponentRenderMode { } + + [DerivedComponentRenderMode] + private class DerivedComponentWithRenderMode : ComponentWithRenderMode + { + class DerivedComponentRenderModeAttribute : RenderModeAttribute + { + public override IComponentRenderMode Mode => new DerivedComponentRenderMode(); + } + } [OwnRenderMode] private class ComponentWithRenderMode : IComponent