Browse Source

Make RouteAttribute non-inherited (#10236)

* Make RouteAttribute non-inherited

Fixes: #5529

Inheriting and looking for inherited route attributes will cause nothing
but trouble. We had a bug tracking what to do about this and we decided
to make it really clear that routes are not inherited.

Previously the attribute was marked as inherited, but we woulnd't look
for inherited routes.

* add test
Ryan Nowak 6 years ago
parent
commit
d794c52c75

+ 1 - 1
src/Components/Components/ref/Microsoft.AspNetCore.Components.netstandard2.0.cs

@@ -398,7 +398,7 @@ namespace Microsoft.AspNetCore.Components
         public System.Threading.Tasks.Task InvokeAsync(System.Func<System.Threading.Tasks.Task> workItem) { throw null; }
         public System.Threading.Tasks.Task InvokeAsync(System.Func<System.Threading.Tasks.Task> workItem) { throw null; }
         public void Render(Microsoft.AspNetCore.Components.RenderFragment renderFragment) { }
         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 partial class RouteAttribute : System.Attribute
     {
     {
         public RouteAttribute(string template) { }
         public RouteAttribute(string template) { }

+ 2 - 2
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.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 
 using System;
 using System;
@@ -8,7 +8,7 @@ namespace Microsoft.AspNetCore.Components
     /// <summary>
     /// <summary>
     /// Indicates that the associated component should match the specified route template pattern.
     /// Indicates that the associated component should match the specified route template pattern.
     /// </summary>
     /// </summary>
-    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
+    [AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = false)]
     public class RouteAttribute : Attribute
     public class RouteAttribute : Attribute
     {
     {
         /// <summary>
         /// <summary>

+ 7 - 2
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.
 // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
 
 
 using System;
 using System;
@@ -23,7 +23,12 @@ namespace Microsoft.AspNetCore.Components.Routing
             var routes = new List<RouteEntry>();
             var routes = new List<RouteEntry>();
             foreach (var type in types)
             foreach (var type in types)
             {
             {
-                var routeAttributes = type.GetCustomAttributes<RouteAttribute>(); // 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<RouteAttribute>(inherit: false);
+
                 foreach (var routeAttribute in routeAttributes)
                 foreach (var routeAttribute in routeAttributes)
                 {
                 {
                     var template = TemplateParser.ParseTemplate(routeAttribute.Template);
                     var template = TemplateParser.ParseTemplate(routeAttribute.Template);

+ 33 - 0
src/Components/Components/test/Routing/RouteTableTests.cs

@@ -11,6 +11,39 @@ namespace Microsoft.AspNetCore.Components.Test.Routing
 {
 {
     public class RouteTableTests
     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]
         [Fact]
         public void CanMatchRootTemplate()
         public void CanMatchRootTemplate()
         {
         {