diff --git a/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.cs b/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.cs index b615df9df1a9..1500126c495a 100644 --- a/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.cs +++ b/src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.cs @@ -398,7 +398,7 @@ public readonly partial struct RenderHandle public System.Threading.Tasks.Task InvokeAsync(System.Func workItem) { throw null; } public void Render(Microsoft.AspNetCore.Components.RenderFragment renderFragment) { } } - [System.AttributeUsageAttribute(System.AttributeTargets.Class, AllowMultiple=true, Inherited=true)] + [System.AttributeUsageAttribute(System.AttributeTargets.Class, AllowMultiple=true, Inherited=false)] public partial class RouteAttribute : System.Attribute { public RouteAttribute(string template) { } diff --git a/src/Components/Components/src/RouteAttribute.cs b/src/Components/Components/src/RouteAttribute.cs index f46d04910fa7..a503dab19554 100644 --- a/src/Components/Components/src/RouteAttribute.cs +++ b/src/Components/Components/src/RouteAttribute.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; @@ -8,7 +8,7 @@ namespace Microsoft.AspNetCore.Components /// /// Indicates that the associated component should match the specified route template pattern. /// - [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)] + [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)] public class RouteAttribute : Attribute { /// diff --git a/src/Components/Components/src/Routing/RouteTable.cs b/src/Components/Components/src/Routing/RouteTable.cs index 0a30f7cbaeba..4449c00f3cc4 100644 --- a/src/Components/Components/src/Routing/RouteTable.cs +++ b/src/Components/Components/src/Routing/RouteTable.cs @@ -1,4 +1,4 @@ -// Copyright (c) .NET Foundation. All rights reserved. +// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; @@ -23,7 +23,12 @@ public static RouteTable Create(IEnumerable types) var routes = new List(); foreach (var type in types) { - var routeAttributes = type.GetCustomAttributes(); // Inherit: true? + // We're deliberately using inherit = false here. + // + // RouteAttribute is defined as non-inherited, because inheriting a route attribute always causes an + // ambiguity. You end up with two components (base class and derived class) with the same route. + var routeAttributes = type.GetCustomAttributes(inherit: false); + foreach (var routeAttribute in routeAttributes) { var template = TemplateParser.ParseTemplate(routeAttribute.Template); diff --git a/src/Components/Components/test/Routing/RouteTableTests.cs b/src/Components/Components/test/Routing/RouteTableTests.cs index a4144dcfaf5e..830024940ec9 100644 --- a/src/Components/Components/test/Routing/RouteTableTests.cs +++ b/src/Components/Components/test/Routing/RouteTableTests.cs @@ -11,6 +11,39 @@ namespace Microsoft.AspNetCore.Components.Test.Routing { public class RouteTableTests { + [Fact] + public void CanDiscoverRoute() + { + // Arrange & Act + var routes = RouteTable.Create(new[] { typeof(MyComponent), }); + + // Assert + Assert.Equal("Test1", Assert.Single(routes.Routes).Template.TemplateText); + } + + [Route("Test1")] + private class MyComponent : ComponentBase + { + } + + [Fact] + public void CanDiscoverRoutes_WithInheritance() + { + // Arrange & Act + var routes = RouteTable.Create(new[] { typeof(MyComponent), typeof(MyInheritedComponent), }); + + // Assert + Assert.Collection( + routes.Routes.OrderBy(r => r.Template.TemplateText), + r => Assert.Equal("Test1", r.Template.TemplateText), + r => Assert.Equal("Test2", r.Template.TemplateText)); + } + + [Route("Test2")] + private class MyInheritedComponent : MyComponent + { + } + [Fact] public void CanMatchRootTemplate() {