diff --git a/src/Microsoft.AspNetCore.Mvc.Core/ApplicationModels/ControllerModel.cs b/src/Microsoft.AspNetCore.Mvc.Core/ApplicationModels/ControllerModel.cs index ddc12f701b..05f6e0a27d 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/ApplicationModels/ControllerModel.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/ApplicationModels/ControllerModel.cs @@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Mvc.ApplicationModels { [DebuggerDisplay("Name={ControllerName}, Type={ControllerType.Name}," + - " Routes: {AttributeRoutes.Count}, Filters: {Filters.Count}")] + " Route: {AttributeRouteModel?.Template}, Filters: {Filters.Count}")] public class ControllerModel : ICommonModel, IFilterModel, IApiExplorerModel { public ControllerModel( @@ -35,7 +35,6 @@ public ControllerModel( Actions = new List(); ApiExplorer = new ApiExplorerModel(); Attributes = new List(attributes); - AttributeRoutes = new List(); ActionConstraints = new List(); Filters = new List(); RouteConstraints = new List(); @@ -66,8 +65,7 @@ public ControllerModel(ControllerModel other) // Make a deep copy of other 'model' types. Actions = new List(other.Actions.Select(a => new ActionModel(a))); ApiExplorer = new ApiExplorerModel(other.ApiExplorer); - AttributeRoutes = new List( - other.AttributeRoutes.Select(a => new AttributeRouteModel(a))); + AttributeRouteModel = new AttributeRouteModel(other.AttributeRouteModel); ControllerProperties = new List(other.ControllerProperties.Select(p => new PropertyModel(p))); } @@ -82,7 +80,7 @@ public ControllerModel(ControllerModel other) /// /// allows configuration of settings for ApiExplorer /// which apply to all actions in the controller unless overridden by . - /// + /// /// Settings applied by override settings from /// . /// @@ -90,7 +88,7 @@ public ControllerModel(ControllerModel other) public ApplicationModel Application { get; set; } - public IList AttributeRoutes { get; private set; } + public AttributeRouteModel AttributeRouteModel { get; set; } public IReadOnlyList Attributes { get; } diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Internal/ControllerActionDescriptorBuilder.cs b/src/Microsoft.AspNetCore.Mvc.Core/Internal/ControllerActionDescriptorBuilder.cs index f458e3a0f2..8eab7063d8 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/Internal/ControllerActionDescriptorBuilder.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/Internal/ControllerActionDescriptorBuilder.cs @@ -55,43 +55,35 @@ public static IList Build(ApplicationModel applicati .ToList(); foreach (var action in controller.Actions) { - // Controllers with multiple [Route] attributes (or user defined implementation of - // IRouteTemplateProvider) will generate one action descriptor per IRouteTemplateProvider - // instance. - // Actions with multiple [Http*] attributes or other (IRouteTemplateProvider implementations - // have already been identified as different actions during action discovery. - var actionDescriptors = CreateActionDescriptors(application, controller, action); - - foreach (var actionDescriptor in actionDescriptors) - { - actionDescriptor.ControllerName = controller.ControllerName; - actionDescriptor.ControllerTypeInfo = controller.ControllerType; + var actionDescriptor = CreateActionDescriptor(application, controller, action); - AddApiExplorerInfo(actionDescriptor, application, controller, action); - AddRouteConstraints(removalConstraints, actionDescriptor, controller, action); - AddProperties(actionDescriptor, action, controller, application); + actionDescriptor.ControllerName = controller.ControllerName; + actionDescriptor.ControllerTypeInfo = controller.ControllerType; - actionDescriptor.BoundProperties = controllerPropertyDescriptors; - if (IsAttributeRoutedAction(actionDescriptor)) - { - hasAttributeRoutes = true; + AddApiExplorerInfo(actionDescriptor, application, controller, action); + AddRouteConstraints(removalConstraints, actionDescriptor, controller, action); + AddProperties(actionDescriptor, action, controller, application); + + actionDescriptor.BoundProperties = controllerPropertyDescriptors; + if (IsAttributeRoutedAction(actionDescriptor)) + { + hasAttributeRoutes = true; - // An attribute routed action will ignore conventional routed constraints. We still - // want to provide these values as ambient values for link generation. - AddConstraintsAsDefaultRouteValues(actionDescriptor); + // An attribute routed action will ignore conventional routed constraints. We still + // want to provide these values as ambient values for link generation. + AddConstraintsAsDefaultRouteValues(actionDescriptor); - // Replaces tokens like [controller]/[action] in the route template with the actual values - // for this action. - ReplaceAttributeRouteTokens(actionDescriptor, routeTemplateErrors); + // Replaces tokens like [controller]/[action] in the route template with the actual values + // for this action. + ReplaceAttributeRouteTokens(actionDescriptor, routeTemplateErrors); - // Attribute routed actions will ignore conventional routed constraints. Instead they have - // a single route constraint "RouteGroup" associated with it. - ReplaceRouteConstraints(actionDescriptor); - } + // Attribute routed actions will ignore conventional routed constraints. Instead they have + // a single route constraint "RouteGroup" associated with it. + ReplaceRouteConstraints(actionDescriptor); } - methodInfoMap.AddToMethodInfo(action, actionDescriptors); - actions.AddRange(actionDescriptors); + methodInfoMap.AddToMethodInfo(action, actionDescriptor); + actions.Add(actionDescriptor); } } @@ -180,12 +172,12 @@ public static IList Build(ApplicationModel applicati return actions; } - private static IList CreateActionDescriptors( + private static ControllerActionDescriptor CreateActionDescriptor( ApplicationModel application, ControllerModel controller, ActionModel action) { - var actionDescriptors = new List(); + ControllerActionDescriptor actionDescriptor; // We check the action to see if the template allows combination behavior // (It doesn't start with / or ~/) so that in the case where we have multiple @@ -195,12 +187,10 @@ private static IList CreateActionDescriptors( { // We're overriding the attribute routes on the controller, so filter out any metadata // from controller level routes. - var actionDescriptor = CreateActionDescriptor( + actionDescriptor = CreateActionDescriptor( action, controllerAttributeRoute: null); - actionDescriptors.Add(actionDescriptor); - // If we're using an attribute route on the controller, then filter out any additional // metadata from the 'other' attribute routes. var controllerFilters = controller.Filters @@ -211,36 +201,28 @@ private static IList CreateActionDescriptors( .Where(c => !(c is IRouteTemplateProvider)); AddActionConstraints(actionDescriptor, action, controllerConstraints); } - else if (controller.AttributeRoutes != null && - controller.AttributeRoutes.Count > 0) + else if (controller.AttributeRouteModel != null) { - // We're using the attribute routes from the controller - foreach (var controllerAttributeRoute in controller.AttributeRoutes) - { - var actionDescriptor = CreateActionDescriptor( - action, - controllerAttributeRoute); + actionDescriptor = CreateActionDescriptor( + action, + controller.AttributeRouteModel); - actionDescriptors.Add(actionDescriptor); - - // If we're using an attribute route on the controller, then filter out any additional - // metadata from the 'other' attribute routes. - var controllerFilters = controller.Filters - .Where(c => c == controllerAttributeRoute?.Attribute || !(c is IRouteTemplateProvider)); - AddActionFilters(actionDescriptor, action.Filters, controllerFilters, application.Filters); + // If we're using an attribute route on the controller, then filter out any additional + // metadata from the 'other' attribute routes. + var controllerFilters = controller.Filters + .Where(c => c == controller.AttributeRouteModel?.Attribute || !(c is IRouteTemplateProvider)); + AddActionFilters(actionDescriptor, action.Filters, controllerFilters, application.Filters); - var controllerConstraints = controller.ActionConstraints - .Where(c => c == controllerAttributeRoute?.Attribute || !(c is IRouteTemplateProvider)); - AddActionConstraints(actionDescriptor, action, controllerConstraints); - } + var controllerConstraints = controller.ActionConstraints + .Where(c => c == controller.AttributeRouteModel?.Attribute || !(c is IRouteTemplateProvider)); + AddActionConstraints(actionDescriptor, action, controllerConstraints); } else { // No attribute routes on the controller - var actionDescriptor = CreateActionDescriptor( + actionDescriptor = CreateActionDescriptor( action, controllerAttributeRoute: null); - actionDescriptors.Add(actionDescriptor); // If there's no attribute route on the controller, then we can use all of the filters/constraints // on the controller. @@ -248,7 +230,7 @@ private static IList CreateActionDescriptors( AddActionConstraints(actionDescriptor, action, controller.ActionConstraints); } - return actionDescriptors; + return actionDescriptor; } private static ControllerActionDescriptor CreateActionDescriptor( @@ -316,15 +298,15 @@ private static void AddApiExplorerInfo( ControllerModel controller, ActionModel action) { - var isVisible = - action.ApiExplorer?.IsVisible ?? - controller.ApiExplorer?.IsVisible ?? + var isVisible = + action.ApiExplorer?.IsVisible ?? + controller.ApiExplorer?.IsVisible ?? application.ApiExplorer?.IsVisible ?? false; - var isVisibleSetOnActionOrController = - action.ApiExplorer?.IsVisible ?? - controller.ApiExplorer?.IsVisible ?? + var isVisibleSetOnActionOrController = + action.ApiExplorer?.IsVisible ?? + controller.ApiExplorer?.IsVisible ?? false; // ApiExplorer isn't supported on conventional-routed actions, but we still allow you to configure @@ -449,7 +431,7 @@ public static void AddRouteConstraints( ControllerModel controller, ActionModel action) { - // Apply all the constraints defined on the action, then controller (for example, [Area]) + // Apply all the constraints defined on the action, then controller (for example, [Area]) // to the actions. Also keep track of all the constraints that require preventing actions // without the constraint to match. For example, actions without an [Area] attribute on their // controller should not match when a value has been given for area when matching a url or @@ -788,17 +770,23 @@ private class MethodToActionMap : { public void AddToMethodInfo( ActionModel action, - IList actionDescriptors) + ControllerActionDescriptor actionDescriptor) { IDictionary> actionsForMethod = null; if (TryGetValue(action.ActionMethod, out actionsForMethod)) { - actionsForMethod.Add(action, actionDescriptors); + IList actionDescriptors; + if (actionsForMethod.TryGetValue(action, out actionDescriptors)) + { + actionDescriptors.Add(actionDescriptor); + } } else { var reflectedActionMap = new Dictionary>(); + var actionDescriptors = new List(); + actionDescriptors.Add(actionDescriptor); reflectedActionMap.Add(action, actionDescriptors); Add(action.ActionMethod, reflectedActionMap); } diff --git a/src/Microsoft.AspNetCore.Mvc.Core/Internal/DefaultApplicationModelProvider.cs b/src/Microsoft.AspNetCore.Mvc.Core/Internal/DefaultApplicationModelProvider.cs index d3785b00d0..0929325026 100644 --- a/src/Microsoft.AspNetCore.Mvc.Core/Internal/DefaultApplicationModelProvider.cs +++ b/src/Microsoft.AspNetCore.Mvc.Core/Internal/DefaultApplicationModelProvider.cs @@ -9,7 +9,6 @@ using Microsoft.AspNetCore.Mvc.ApiExplorer; using Microsoft.AspNetCore.Mvc.ApplicationModels; using Microsoft.AspNetCore.Mvc.Filters; -using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.AspNetCore.Mvc.ModelBinding; using Microsoft.AspNetCore.Mvc.Routing; using Microsoft.Extensions.Internal; @@ -51,43 +50,47 @@ public virtual void OnProvidersExecuting(ApplicationModelProviderContext context foreach (var controllerType in context.ControllerTypes) { var controllerModels = BuildControllerModels(controllerType); - if (controllerModels != null) + if (controllerModels == null) { - foreach (var controllerModel in controllerModels) + continue; + } + + foreach (var controllerModel in controllerModels) + { + context.Result.Controllers.Add(controllerModel); + controllerModel.Application = context.Result; + + foreach (var propertyHelper in PropertyHelper.GetProperties(controllerType.AsType())) { - context.Result.Controllers.Add(controllerModel); - controllerModel.Application = context.Result; + var propertyInfo = propertyHelper.Property; + var propertyModel = CreatePropertyModel(propertyInfo); + if (propertyModel != null) + { + propertyModel.Controller = controllerModel; + controllerModel.ControllerProperties.Add(propertyModel); + } + } - foreach (var propertyHelper in PropertyHelper.GetProperties(controllerType.AsType())) + foreach (var methodInfo in controllerType.AsType().GetMethods()) + { + var actionModels = BuildActionModels(controllerType, methodInfo); + if (actionModels == null) { - var propertyInfo = propertyHelper.Property; - var propertyModel = CreatePropertyModel(propertyInfo); - if (propertyModel != null) - { - propertyModel.Controller = controllerModel; - controllerModel.ControllerProperties.Add(propertyModel); - } + continue; } - foreach (var methodInfo in controllerType.AsType().GetMethods()) + foreach (var actionModel in actionModels) { - var actionModels = BuildActionModels(controllerType, methodInfo); - if (actionModels != null) + actionModel.Controller = controllerModel; + controllerModel.Actions.Add(actionModel); + + foreach (var parameterInfo in actionModel.ActionMethod.GetParameters()) { - foreach (var actionModel in actionModels) + var parameterModel = CreateParameterModel(parameterInfo); + if (parameterModel != null) { - actionModel.Controller = controllerModel; - controllerModel.Actions.Add(actionModel); - - foreach (var parameterInfo in actionModel.ActionMethod.GetParameters()) - { - var parameterModel = CreateParameterModel(parameterInfo); - if (parameterModel != null) - { - parameterModel.Action = actionModel; - actionModel.Parameters.Add(parameterModel); - } - } + parameterModel.Action = actionModel; + actionModel.Parameters.Add(parameterModel); } } } @@ -117,8 +120,7 @@ protected virtual IEnumerable BuildControllerModels(TypeInfo ty throw new ArgumentNullException(nameof(typeInfo)); } - var controllerModel = CreateControllerModel(typeInfo); - yield return controllerModel; + return CreateControllerModels(typeInfo); } /// @@ -126,7 +128,7 @@ protected virtual IEnumerable BuildControllerModels(TypeInfo ty /// /// The . /// A for the given . - protected virtual ControllerModel CreateControllerModel(TypeInfo typeInfo) + protected virtual IList CreateControllerModels(TypeInfo typeInfo) { if (typeInfo == null) { @@ -181,10 +183,36 @@ protected virtual ControllerModel CreateControllerModel(TypeInfo typeInfo) attributes = filteredAttributes.ToArray(); - var controllerModel = new ControllerModel(typeInfo, attributes); - AddRange( - controllerModel.AttributeRoutes, routeAttributes.Select(a => new AttributeRouteModel(a))); + var controllerModels = new List(); + if (routeAttributes.Length == 0) + { + controllerModels.Add(CreateControllerModel(typeInfo, attributes, routeAttribute: null)); + } + else + { + // + // Controllers with multiple [Route] attributes (or user defined implementation of + // IRouteTemplateProvider) will generate one action descriptor per IRouteTemplateProvider + // instance. + // Actions with multiple [Http*] attributes or other (IRouteTemplateProvider implementations + // have already been identified as different actions during action discovery. + for (var i = 0; i < routeAttributes.Length; i++) + { + controllerModels.Add(CreateControllerModel(typeInfo, attributes, routeAttributes[i])); + } + } + return controllerModels; + } + + private ControllerModel CreateControllerModel( + TypeInfo typeInfo, + object[] attributes, + IRouteTemplateProvider routeAttribute) + { + var controllerModel = new ControllerModel(typeInfo, attributes); + controllerModel.AttributeRouteModel = + (routeAttribute == null) ? null : new AttributeRouteModel(routeAttribute); controllerModel.ControllerName = typeInfo.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) ? typeInfo.Name.Substring(0, typeInfo.Name.Length - "Controller".Length) : @@ -248,9 +276,8 @@ protected virtual PropertyModel CreatePropertyModel(PropertyInfo propertyInfo) return propertyModel; } - /// - /// Creates the instances for the given action . + /// Creates the instances for the given action . /// /// The controller . /// The action . diff --git a/src/Microsoft.AspNetCore.Mvc.WebApiCompatShim/Conventions/WebApiActionConventionsApplicationModelConvention.cs b/src/Microsoft.AspNetCore.Mvc.WebApiCompatShim/Conventions/WebApiActionConventionsApplicationModelConvention.cs index 3e04f370f7..f144f985d5 100644 --- a/src/Microsoft.AspNetCore.Mvc.WebApiCompatShim/Conventions/WebApiActionConventionsApplicationModelConvention.cs +++ b/src/Microsoft.AspNetCore.Mvc.WebApiCompatShim/Conventions/WebApiActionConventionsApplicationModelConvention.cs @@ -64,7 +64,7 @@ private bool IsConventionApplicable(ControllerModel controller) private bool IsActionAttributeRouted(ActionModel action) { - if (action.Controller.AttributeRoutes.Count > 0) + if (action.Controller.AttributeRouteModel != null) { return true; } diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/ApplicationModel/ControllerModelTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/ApplicationModel/ControllerModelTest.cs index 1b640f3c33..bae1475c55 100644 --- a/test/Microsoft.AspNetCore.Mvc.Core.Test/ApplicationModel/ControllerModelTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/ApplicationModel/ControllerModelTest.cs @@ -28,7 +28,7 @@ public void CopyConstructor_DoesDeepCopyOfOtherModels() action.Controller = controller; var route = new AttributeRouteModel(new HttpGetAttribute("api/Products")); - controller.AttributeRoutes.Add(route); + controller.AttributeRouteModel = route; var apiExplorer = controller.ApiExplorer; controller.ApiExplorer.GroupName = "group"; @@ -39,7 +39,7 @@ public void CopyConstructor_DoesDeepCopyOfOtherModels() // Assert Assert.NotSame(action, controller2.Actions[0]); - Assert.NotSame(route, controller2.AttributeRoutes[0]); + Assert.NotSame(route, controller2.AttributeRouteModel); Assert.NotSame(apiExplorer, controller2.ApiExplorer); Assert.NotSame(controller.ActionConstraints, controller2.ActionConstraints); diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ControllerActionDescriptorProviderTests.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ControllerActionDescriptorProviderTests.cs index c4f7ad4863..3a4fa9c1b3 100644 --- a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ControllerActionDescriptorProviderTests.cs +++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/ControllerActionDescriptorProviderTests.cs @@ -416,13 +416,13 @@ public void BuildModel_CreatesControllerModels_ForAllControllers() var conventional = Assert.Single(model.Controllers, c => c.ControllerName == "ConventionallyRouted"); - Assert.Empty(conventional.AttributeRoutes); + Assert.Null(conventional.AttributeRouteModel); Assert.Single(conventional.Actions); var attributeRouted = Assert.Single(model.Controllers, c => c.ControllerName == "AttributeRouted"); Assert.Single(attributeRouted.Actions); - Assert.Single(attributeRouted.AttributeRoutes); + Assert.NotNull(attributeRouted.AttributeRouteModel); var empty = Assert.Single(model.Controllers, c => c.ControllerName == "Empty"); @@ -486,8 +486,8 @@ public void AttributeRouting_TokenReplacement_IsAfterReflectedModel() // Assert var controller = Assert.Single(model.Controllers); - var attributeRouteModel = Assert.Single(controller.AttributeRoutes); - Assert.Equal("api/Token/[key]/[controller]", attributeRouteModel.Template); + Assert.NotNull(controller.AttributeRouteModel); + Assert.Equal("api/Token/[key]/[controller]", controller.AttributeRouteModel.Template); var action = Assert.Single(controller.Actions); Assert.Equal("stub/[action]", action.AttributeRouteModel.Template); diff --git a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/DefaultApplicationModelProviderTest.cs b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/DefaultApplicationModelProviderTest.cs index 3978cf66c6..9224427c68 100644 --- a/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/DefaultApplicationModelProviderTest.cs +++ b/test/Microsoft.AspNetCore.Mvc.Core.Test/Internal/DefaultApplicationModelProviderTest.cs @@ -26,9 +26,10 @@ public void CreateControllerModel_DerivedFromControllerClass_HasFilter() var typeInfo = typeof(StoreController).GetTypeInfo(); // Act - var model = builder.CreateControllerModel(typeInfo); + var models = builder.BuildControllerModels(typeInfo); // Assert + var model = Assert.Single(models); var filter = Assert.Single(model.Filters); Assert.IsType(filter); } @@ -65,9 +66,10 @@ public void CreateControllerModel_ClassWithoutFilterInterfaces_HasNoControllerFi var typeInfo = typeof(NoFiltersController).GetTypeInfo(); // Act - var model = builder.CreateControllerModel(typeInfo); + var models = builder.BuildControllerModels(typeInfo); // Assert + var model = Assert.Single(models); var filter = Assert.Single(model.Filters); Assert.IsType(filter); } @@ -80,9 +82,10 @@ public void CreateControllerModel_ClassWithFilterInterfaces_HasFilter() var typeInfo = typeof(SomeFiltersController).GetTypeInfo(); // Act - var model = builder.CreateControllerModel(typeInfo); + var models = builder.BuildControllerModels(typeInfo); // Assert + var model = Assert.Single(models); Assert.Single(model.Filters, f => f is ControllerActionFilter); Assert.Single(model.Filters, f => f is ControllerResultFilter); } @@ -95,9 +98,10 @@ public void CreateControllerModel_ClassWithFilterInterfaces_UnsupportedType() var typeInfo = typeof(UnsupportedFiltersController).GetTypeInfo(); // Act - var model = builder.CreateControllerModel(typeInfo); + var models = builder.BuildControllerModels(typeInfo); // Assert + var model = Assert.Single(models); Assert.Empty(model.Filters); } @@ -109,17 +113,21 @@ public void CreateControllerModel_ClassWithInheritedRoutes() var typeInfo = typeof(DerivedClassInheritingRoutesController).GetTypeInfo(); // Act - var model = builder.CreateControllerModel(typeInfo); + var models = builder.BuildControllerModels(typeInfo)?.ToArray(); // Assert - Assert.Equal(2, model.AttributeRoutes.Count); + Assert.NotNull(models); + Assert.Equal(2, models.Length); + var model = models[0]; Assert.Equal(2, model.Attributes.Count); - var route = Assert.Single(model.AttributeRoutes, r => r.Template == "A"); - Assert.Contains(route.Attribute, model.Attributes); + Assert.NotNull(model.AttributeRouteModel); + Assert.Equal("A", model.AttributeRouteModel.Template); + Assert.Contains(model.AttributeRouteModel.Attribute, model.Attributes); - route = Assert.Single(model.AttributeRoutes, r => r.Template == "B"); - Assert.Contains(route.Attribute, model.Attributes); + model = models[1]; + Assert.Equal("B", model.AttributeRouteModel.Template); + Assert.Contains(model.AttributeRouteModel.Attribute, model.Attributes); } [Fact] @@ -130,17 +138,21 @@ public void CreateControllerModel_ClassWithHiddenInheritedRoutes() var typeInfo = typeof(DerivedClassHidingRoutesController).GetTypeInfo(); // Act - var model = builder.CreateControllerModel(typeInfo); + var models = builder.BuildControllerModels(typeInfo)?.ToArray(); // Assert - Assert.Equal(2, model.AttributeRoutes.Count); + Assert.NotNull(models); + Assert.Equal(2, models.Length); + + var model = models[0]; Assert.Equal(2, model.Attributes.Count); - var route = Assert.Single(model.AttributeRoutes, r => r.Template == "C"); - Assert.Contains(route.Attribute, model.Attributes); + Assert.Equal("C", model.AttributeRouteModel.Template); + Assert.Contains(model.AttributeRouteModel.Attribute, model.Attributes); - route = Assert.Single(model.AttributeRoutes, r => r.Template == "D"); - Assert.Contains(route.Attribute, model.Attributes); + model = models[1]; + Assert.Equal("D", model.AttributeRouteModel.Template); + Assert.Contains(model.AttributeRouteModel.Attribute, model.Attributes); } [Theory] @@ -1228,11 +1240,6 @@ public TestApplicationModelProvider( return base.BuildControllerModels(typeInfo); } - public new ControllerModel CreateControllerModel(TypeInfo typeInfo) - { - return base.CreateControllerModel(typeInfo); - } - public new PropertyModel CreatePropertyModel(PropertyInfo propertyInfo) { return base.CreatePropertyModel(propertyInfo);