/// <summary>
        /// Maps the attribute-defined routes for the application.
        /// </summary>
        /// <param name="routes"></param>
        /// <param name="controllerTypes">The controller types to scan.</param>
        /// <param name="constraintResolver">
        /// The <see cref="IInlineConstraintResolver"/> to use for resolving inline constraints in route templates.
        /// </param>
        public static void MapAttributeRoutes(RouteCollection routes, IEnumerable<Type> controllerTypes,
            IInlineConstraintResolver constraintResolver)
        {
            SubRouteCollection subRoutes = new SubRouteCollection();
            AddRouteEntries(subRoutes, controllerTypes, constraintResolver);
            IReadOnlyCollection<RouteEntry> entries = subRoutes.Entries;

            if (entries.Count > 0)
            {
                RouteCollectionRoute aggregrateRoute = new RouteCollectionRoute(subRoutes);
                routes.Add(aggregrateRoute);

                // This sort is here to enforce a static ordering for link generation using these routes. 
                // We don't apply dynamic criteria like ActionSelectors on link generation, but we can use the static
                // ones.
                //
                // Routes to actions are placed first because they are considered more specific. A route to an action
                // will only match for link generation if the action name was supplied, so this is essential for
                // correctness. Without this a controller-level route could be 'greedy' and generate a link when
                // the action-level route was intended.
                RouteEntry[] sorted = entries
                    .OrderBy(r => r.Route.GetOrder())
                    .ThenBy(r => r.Route.GetTargetIsAction() ? 0 : 1)
                    .ThenBy(r => r.Route.GetPrecedence())
                    .ToArray();

                AddGenerationHooksForSubRoutes(routes, sorted);
            }
        }
        public static void MapAttributeRoutes(
            HttpConfiguration configuration,
            IInlineConstraintResolver constraintResolver,
            IDirectRouteProvider directRouteProvider)
        {
            if (configuration == null)
            {
                throw new ArgumentNullException("configuration");
            }

            if (constraintResolver == null)
            {
                throw new ArgumentNullException("constraintResolver");
            }

            if (directRouteProvider == null)
            {
                throw new ArgumentNullException("directRouteProvider");
            }

            RouteCollectionRoute aggregateRoute = new RouteCollectionRoute();
            configuration.Routes.Add(AttributeRouteName, aggregateRoute);

            Action<HttpConfiguration> previousInitializer = configuration.Initializer;
            configuration.Initializer = config =>
                {
                    // Chain to the previous initializer hook. Do this before we access the config since
                    // initialization may make last minute changes to the configuration.
                    previousInitializer(config);

                    SubRouteCollection subRoutes = null;

                    // Add a single placeholder route that handles all of attribute routing.
                    // Add an initialize hook that initializes these routes after the config has been initialized.
                    Func<SubRouteCollection> initializer = () =>
                    {
                        subRoutes = new SubRouteCollection();
                        AddRouteEntries(subRoutes, configuration, constraintResolver, directRouteProvider);
                        return subRoutes;
                    };

                    // This won't change config. It wants to pick up the finalized config.
                    aggregateRoute.EnsureInitialized(initializer);

                    if (subRoutes != null)
                    {
                        AddGenerationHooksForSubRoutes(config.Routes, subRoutes.Entries);
                    }
                };
        }
        public void SubRouteCollection_Throws_OnDuplicateNamedRoute_MVC()
        {
            // Arrange
            var collection = new SubRouteCollection();
            var route1 = new Route("Home/Index", new Mock<IRouteHandler>().Object);
            var route2 = new Route("Person/Index", new Mock<IRouteHandler>().Object);

            collection.Add(new RouteEntry("route", route1));

            var expectedError =
                "A route named 'route' is already in the route collection. Route names must be unique.\r\n\r\n" +
                "Duplicates:" + Environment.NewLine +
                "Person/Index" + Environment.NewLine +
                "Home/Index";

            // Act & Assert
            Assert.Throws<InvalidOperationException>(() => collection.Add(new RouteEntry("route", route2)), expectedError);
        }
        public void SubRouteCollection_Throws_OnDuplicateNamedRoute_WebAPI()
        {
            // Arrange
            var collection = new SubRouteCollection();
            var route1 = new HttpRoute("api/Person");
            var route2 = new HttpRoute("api/Car");

            collection.Add(new RouteEntry("route", route1));

            var expectedError =
                "A route named 'route' is already in the route collection. Route names must be unique.\r\n\r\n" +
                "Duplicates:" + Environment.NewLine +
                "api/Car" + Environment.NewLine +
                "api/Person";

            // Act & Assert
            Assert.Throws<InvalidOperationException>(() => collection.Add(new RouteEntry("route", route2)), expectedError);
        }
        public void GetControllerType_WithMultipleDirectRouteControllers_ThrowsInvalidOperationException()
        {
            // Arrange
            var requestContext = new RequestContext();
            requestContext.RouteData = new RouteData();
            SubRouteCollection subRoutes = new SubRouteCollection();
            DirectRouteTestHelpers.AddDirectRouteFromController<AttributeRouteAtControllerLevelController>(subRoutes);
            DirectRouteTestHelpers.AddDirectRouteFromMethod<WithRoutingAttributeController>(subRoutes, c => c.Action());
            requestContext.RouteData.Route = new RouteCollectionRoute(subRoutes);
            requestContext.RouteData.AddDirectRouteMatches();

            var controllerActivator = new Mock<IControllerActivator>(MockBehavior.Strict).Object;
            var activatorResolver = new Resolver<IControllerActivator>();

            var factory = new DefaultControllerFactory(controllerActivator, activatorResolver, null);

            // Act & Assert
            Assert.Throws<InvalidOperationException>(() => factory.GetControllerType(requestContext, controllerName: null));
        }
 private static void AddRouteEntries(SubRouteCollection collector, string areaPrefix, string prefix,
     IEnumerable<IDirectRouteFactory> factories, IReadOnlyCollection<ActionDescriptor> actions,
     IInlineConstraintResolver constraintResolver, bool targetIsAction)
 {
     foreach (IDirectRouteFactory factory in factories)
     {
         RouteEntry entry = CreateRouteEntry(areaPrefix, prefix, factory, actions, constraintResolver,
             targetIsAction);
         collector.Add(entry);
     }
 }
        internal static void AddRouteEntries(SubRouteCollection collector,
            ReflectedAsyncControllerDescriptor controller, IInlineConstraintResolver constraintResolver)
        {
            string prefix = GetRoutePrefix(controller);

            RouteAreaAttribute area = controller.GetAreaFrom();
            string areaName = controller.GetAreaName(area);
            string areaPrefix = area != null ? area.AreaPrefix ?? area.AreaName : null;
            ValidateAreaPrefixTemplate(areaPrefix, areaName, controller);

            AsyncActionMethodSelector actionSelector = controller.Selector;

            foreach (var method in actionSelector.DirectRouteMethods)
            {
                ActionDescriptor action = CreateActionDescriptor(controller, actionSelector, method);

                IEnumerable<IDirectRouteFactory> factories = GetRouteFactories(method, controller.ControllerType);

                AddRouteEntries(collector, areaPrefix, prefix, factories, new ActionDescriptor[] { action },
                    constraintResolver, targetIsAction: true);
            }

            // Check for controller-level routes. 
            List<ActionDescriptor> actionsWithoutRoutes = new List<ActionDescriptor>();

            foreach (var method in actionSelector.StandardRouteMethods)
            {
                ActionDescriptor action = CreateActionDescriptor(controller, actionSelector, method);

                actionsWithoutRoutes.Add(action);
            }

            IReadOnlyCollection<IDirectRouteFactory> controllerFactories = GetRouteFactories(controller);

            // If they exist and have not been overridden, create routes for controller-level route providers.
            if (controllerFactories.Count > 0 && actionsWithoutRoutes.Count > 0)
            {
                AddRouteEntries(collector, areaPrefix, prefix, controllerFactories, actionsWithoutRoutes,
                    constraintResolver, targetIsAction: false);
            }
        }
 internal static IReadOnlyCollection<RouteEntry> MapAttributeRoutes(
     ReflectedAsyncControllerDescriptor controller)
 {
     SubRouteCollection collector = new SubRouteCollection();
     AddRouteEntries(collector, controller, new DefaultInlineConstraintResolver());
     return collector.Entries;
 }
        internal static void AddRouteEntries(SubRouteCollection collector, IEnumerable<Type> controllerTypes,
            IInlineConstraintResolver constraintResolver)
        {
            ControllerDescriptorCache descriptorsCache = new AsyncControllerActionInvoker().DescriptorCache;
            IEnumerable<ReflectedAsyncControllerDescriptor> descriptors = controllerTypes
                .Select(
                    type =>
                    descriptorsCache.GetDescriptor(type, innerType => new ReflectedAsyncControllerDescriptor(innerType), type))
                .Cast<ReflectedAsyncControllerDescriptor>();

            foreach (ReflectedAsyncControllerDescriptor controllerDescriptor in descriptors)
            {
                AddRouteEntries(collector, controllerDescriptor, constraintResolver);
            }
        }
        internal static void AddRouteEntries(SubRouteCollection collector, IEnumerable<Type> controllerTypes,
            IInlineConstraintResolver constraintResolver, IDirectRouteProvider directRouteProvider)
        {
            IEnumerable<ReflectedAsyncControllerDescriptor> controllers = GetControllerDescriptors(controllerTypes);

            foreach (ReflectedAsyncControllerDescriptor controller in controllers)
            {
                List<ActionDescriptor> actions = GetActionDescriptors(controller);

                IReadOnlyCollection<RouteEntry> entries = directRouteProvider.GetDirectRoutes(controller, actions, constraintResolver);
                if (entries == null)
                {
                    throw Error.InvalidOperation(
                        MvcResources.TypeMethodMustNotReturnNull,
                        typeof(IDirectRouteProvider).Name, 
                        "GetDirectRoutes");
                }

                foreach (RouteEntry entry in entries)
                {
                    if (entry == null)
                    {
                        throw Error.InvalidOperation(
                            MvcResources.TypeMethodMustNotReturnNull,
                            typeof(IDirectRouteProvider).Name, 
                            "GetDirectRoutes");
                    }

                    DirectRouteBuilder.ValidateRouteEntry(entry);

                    // This marks the action/controller as unreachable via traditional routing.
                    if (entry.Route.GetTargetIsAction())
                    {
                        var actionDescriptors = entry.Route.GetTargetActionDescriptors();
                        Contract.Assert(actionDescriptors != null && actionDescriptors.Any());

                        foreach (var actionDescriptor in actionDescriptors.OfType<IMethodInfoActionDescriptor>())
                        {
                            var methodInfo = actionDescriptor.MethodInfo;
                            if (methodInfo != null)
                            {
                                controller.Selector.StandardRouteMethods.Remove(methodInfo);
                            }
                        }
                    }
                    else
                    {
                        // This is a controller-level route - no actions in this controller are reachable via
                        // traditional routes.
                        controller.Selector.StandardRouteMethods.Clear();
                    }
                }

                collector.AddRange(entries);
            }
        }
        // just for testing
        internal static IReadOnlyCollection<RouteEntry> GetAttributeRoutes(Type controllerType)
        {
            SubRouteCollection collector = new SubRouteCollection();
            AddRouteEntries(
                collector, 
                new Type[] { controllerType }, 
                new DefaultInlineConstraintResolver(), 
                new DefaultDirectRouteProvider());

            return collector.Entries;
        }
        private static void AddRouteEntries(SubRouteCollection collector, HttpConfiguration configuration,
            IInlineConstraintResolver constraintResolver)
        {
            Contract.Assert(configuration != null);

            IHttpControllerSelector controllerSelector = configuration.Services.GetHttpControllerSelector();
            IDictionary<string, HttpControllerDescriptor> controllerMap = controllerSelector.GetControllerMapping();
            if (controllerMap != null)
            {
                foreach (HttpControllerDescriptor controllerDescriptor in controllerMap.Values)
                {
                    AddRouteEntries(collector, controllerDescriptor, constraintResolver);
                }
            }
        }
        private static void AddRouteEntries(SubRouteCollection collector, HttpControllerDescriptor controller,
            IInlineConstraintResolver constraintResolver)
        {
            IHttpActionSelector actionSelector = controller.Configuration.Services.GetActionSelector();
            ILookup<string, HttpActionDescriptor> actionMap = actionSelector.GetActionMapping(controller);
            if (actionMap == null)
            {
                return;
            }

            string prefix = GetRoutePrefix(controller);
            List<ReflectedHttpActionDescriptor> actionsWithoutRoutes = new List<ReflectedHttpActionDescriptor>();

            foreach (IGrouping<string, HttpActionDescriptor> actionGrouping in actionMap)
            {
                foreach (ReflectedHttpActionDescriptor action in actionGrouping.OfType<ReflectedHttpActionDescriptor>())
                {
                    IReadOnlyCollection<IDirectRouteFactory> factories = GetRouteFactories(action);

                    // Ignore the Route attributes from inherited actions.
                    if (action.MethodInfo != null &&
                        action.MethodInfo.DeclaringType != controller.ControllerType)
                    {
                        factories = null;
                    }

                    if (factories != null && factories.Count > 0)
                    {
                        AddRouteEntries(collector, prefix, factories,
                            new ReflectedHttpActionDescriptor[] { action }, constraintResolver, targetIsAction: true);
                    }
                    else
                    {
                        // IF there are no routes on the specific action, attach it to the controller routes (if any).
                        actionsWithoutRoutes.Add(action);
                    }
                }
            }

            IReadOnlyCollection<IDirectRouteFactory> controllerFactories = GetRouteFactories(controller);

            // If they exist and have not been overridden, create routes for controller-level route providers.
            if (controllerFactories.Count > 0 && actionsWithoutRoutes.Count > 0)
            {
                AddRouteEntries(collector, prefix, controllerFactories, actionsWithoutRoutes,
                    constraintResolver, targetIsAction: false);
            }
        }
        private static void AddRouteEntries(
            SubRouteCollection collector, 
            HttpConfiguration configuration,
            IInlineConstraintResolver constraintResolver,
            IDirectRouteProvider directRouteProvider)
        {
            Contract.Assert(configuration != null);
            Contract.Assert(directRouteProvider != null);

            IHttpControllerSelector controllerSelector = configuration.Services.GetHttpControllerSelector();
            IDictionary<string, HttpControllerDescriptor> controllerMap = controllerSelector.GetControllerMapping();
            if (controllerMap != null)
            {
                foreach (HttpControllerDescriptor controllerDescriptor in controllerMap.Values)
                {
                    IHttpActionSelector actionSelector = controllerDescriptor.Configuration.Services.GetActionSelector();

                    ILookup<string, HttpActionDescriptor> actionsByName =
                        actionSelector.GetActionMapping(controllerDescriptor);
                    if (actionsByName == null)
                    {
                        continue;
                    }

                    List<HttpActionDescriptor> actions = actionsByName.SelectMany(g => g).ToList();
                    IReadOnlyCollection<RouteEntry> newEntries =
                        directRouteProvider.GetDirectRoutes(controllerDescriptor, actions, constraintResolver);
                    if (newEntries == null)
                    {
                        throw Error.InvalidOperation(
                            SRResources.TypeMethodMustNotReturnNull,
                            typeof(IDirectRouteProvider).Name, "GetDirectRoutes");
                    }

                    foreach (RouteEntry entry in newEntries)
                    {
                        if (entry == null)
                        {
                            throw Error.InvalidOperation(
                                SRResources.TypeMethodMustNotReturnNull,
                                typeof(IDirectRouteProvider).Name, "GetDirectRoutes");
                        }

                        DirectRouteBuilder.ValidateRouteEntry(entry);

                        // We need to mark each action as only reachable by direct routes so that traditional routes
                        // don't accidentally hit them.
                        HttpControllerDescriptor routeControllerDescriptor = entry.Route.GetTargetControllerDescriptor();
                        if (routeControllerDescriptor == null)
                        {
                            HttpActionDescriptor[] actionDescriptors = entry.Route.GetTargetActionDescriptors();
                            foreach (var actionDescriptor in actionDescriptors)
                            {
                                actionDescriptor.SetIsAttributeRouted(true);
                            }
                        }
                        else
                        {
                            routeControllerDescriptor.SetIsAttributeRouted(true);
                        }                        
                    }

                    collector.AddRange(newEntries);
                }
            }
        }