示例#1
0
        private IDictionary <ODataPathTemplate, HttpActionDescriptor> BuildAttributeMappings(IEnumerable <HttpControllerDescriptor> controllers)
        {
            Dictionary <ODataPathTemplate, HttpActionDescriptor> attributeMappings =
                new Dictionary <ODataPathTemplate, HttpActionDescriptor>();

            foreach (HttpControllerDescriptor controller in controllers)
            {
                if (IsODataController(controller) && ShouldMapController(controller))
                {
                    IHttpActionSelector actionSelector = controller.Configuration.Services.GetActionSelector();
                    ILookup <string, HttpActionDescriptor> actionMapping = actionSelector.GetActionMapping(controller);
                    HttpActionDescriptor[] actions = actionMapping.SelectMany(a => a).ToArray();

                    foreach (string prefix in GetODataRoutePrefixes(controller))
                    {
                        foreach (HttpActionDescriptor action in actions)
                        {
                            IEnumerable <ODataPathTemplate> pathTemplates = GetODataPathTemplates(prefix, action);
                            foreach (ODataPathTemplate pathTemplate in pathTemplates)
                            {
                                attributeMappings.Add(pathTemplate, action);
                            }
                        }
                    }
                }
            }

            return(attributeMappings);
        }
        public void GetBinding_Wraps_FormatterParameterBinding(bool tracingEnabled)
        {
            // Arrange
            HttpConfiguration config = new HttpConfiguration();

            config.Services.Replace(
                typeof(IAssembliesResolver),
                new TestAssemblyResolver(new MockAssembly(typeof(PerRequestActionValueBinderTestSampleController))));

            if (tracingEnabled)
            {
                config.Services.Replace(typeof(ITraceWriter), new Mock <ITraceWriter>().Object);
                ITraceManager traceManager = config.Services.GetTraceManager();
                traceManager.Initialize(config);
            }

            IHttpControllerSelector controllerSelector = config.Services.GetHttpControllerSelector();
            IHttpActionSelector     actionSelector     = config.Services.GetActionSelector();

            HttpControllerDescriptor controllerDescriptor = controllerSelector.GetControllerMapping()["PerRequestActionValueBinderTestSample"];
            HttpActionDescriptor     actionDescriptor     = actionSelector.GetActionMapping(controllerDescriptor)["Post"].Single();

            PerRequestActionValueBinder binder = new PerRequestActionValueBinder(new DefaultActionValueBinder());

            // Act
            HttpActionBinding binding = binder.GetBinding(actionDescriptor);

            // Assert
            HttpParameterBinding parameterBinding = binding.ParameterBindings.Where(p => p.Descriptor.ParameterName == "customer").Single();

            Assert.True(parameterBinding is PerRequestParameterBinding);
        }
        void ExploreRouteActions(
            IHttpRoute route,
            string localPath,
            HttpControllerDescriptor controllerDescriptor,
            IHttpActionSelector actionSelector,
            Collection <VersionedApiDescription> apiDescriptions,
            ApiVersion apiVersion)
        {
            Contract.Requires(route != null);
            Contract.Requires(controllerDescriptor != null);
            Contract.Requires(actionSelector != null);
            Contract.Requires(apiDescriptions != null);
            Contract.Requires(apiVersion != null);

            var actionMapping = actionSelector.GetActionMapping(controllerDescriptor);

            if (actionMapping == null)
            {
                return;
            }

            const string ActionRouteParameterName = null;

            foreach (var grouping in actionMapping)
            {
                foreach (var action in grouping)
                {
                    if (ShouldExploreAction(ActionRouteParameterName, action, route, apiVersion))
                    {
                        PopulateActionDescriptions(action, route, localPath, apiDescriptions, apiVersion);
                    }
                }
            }
        }
        private static IDictionary <ODataPathTemplate, HttpActionDescriptor> BuildAttributeMappings(ICollection <HttpControllerDescriptor> controllers,
                                                                                                    AttributeRoutingConvention routingConvention)
        {
            IDictionary <ODataPathTemplate, HttpActionDescriptor> attributeMappings =
                new Dictionary <ODataPathTemplate, HttpActionDescriptor>();

            foreach (HttpControllerDescriptor controller in controllers)
            {
                if (IsODataController(controller) && ShouldMapController(controller))
                {
                    IHttpActionSelector actionSelector = controller.Configuration.Services.GetActionSelector();
                    ILookup <string, HttpActionDescriptor> actionMapping = actionSelector.GetActionMapping(controller);
                    HttpActionDescriptor[] actions = actionMapping.SelectMany(a => a).ToArray();

                    foreach (string prefix in GetODataRoutePrefixes(controller))
                    {
                        foreach (HttpActionDescriptor action in actions)
                        {
                            IEnumerable <ODataPathTemplate> pathTemplates = // Invoke private method
                                                                            routingConvention.InvokeFunction <IEnumerable <ODataPathTemplate> >("GetODataPathTemplates", prefix, action);
                            foreach (ODataPathTemplate pathTemplate in pathTemplates)
                            {
                                //attributeMappings.Add(pathTemplate, new WebApiActionDescriptor(action));
                                attributeMappings.Add(pathTemplate, action);
                            }
                        }
                    }
                }
            }

            return(attributeMappings);
        }
示例#5
0
 /// <summary>
 /// Returns a map, keyed by action string, of all System.Web.Http.Controllers.HttpActionDescriptor
 /// that the selector can select. This is primarily called by System.Web.Http.Description.IApiExplorer
 /// to discover all the possible actions in the controller.
 /// </summary>
 /// <param name="controllerDescriptor">The controller descriptor.</param>
 /// <returns>
 /// A map of System.Web.Http.Controllers.HttpActionDescriptor that the selector
 /// can select, or null if the selector does not have a well-defined mapping
 /// of System.Web.Http.Controllers.HttpActionDescriptor.
 /// </returns>
 public ILookup <string, HttpActionDescriptor> GetActionMapping(HttpControllerDescriptor controllerDescriptor)
 {
     if (_innerSelector != null)
     {
         var result = _innerSelector.GetActionMapping(controllerDescriptor);
         return(result);
     }
     throw new NotImplementedException();
 }
        private static IEnumerable <HttpRouteEntry> CreateRouteEntries(HttpControllerDescriptor controllerDescriptor)
        {
            IHttpActionSelector actionSelector = controllerDescriptor.Configuration.Services.GetActionSelector();
            ILookup <string, HttpActionDescriptor> actionMap = actionSelector.GetActionMapping(controllerDescriptor);

            if (actionMap == null)
            {
                return(Enumerable.Empty <HttpRouteEntry>());
            }

            List <HttpRouteEntry> routes = new List <HttpRouteEntry>();
            string routePrefix           = GetRoutePrefix(controllerDescriptor);
            List <ReflectedHttpActionDescriptor> actionsWithoutRoutes = new List <ReflectedHttpActionDescriptor>();

            foreach (IGrouping <string, HttpActionDescriptor> actionGrouping in actionMap)
            {
                string actionName = actionGrouping.Key;

                foreach (ReflectedHttpActionDescriptor actionDescriptor in actionGrouping.OfType <ReflectedHttpActionDescriptor>())
                {
                    Collection <IHttpRouteInfoProvider> routeProviders = actionDescriptor.GetCustomAttributes <IHttpRouteInfoProvider>(inherit: false);

                    // Ignore the Route attributes from inherited actions.
                    if (actionDescriptor.MethodInfo != null &&
                        actionDescriptor.MethodInfo.DeclaringType != controllerDescriptor.ControllerType)
                    {
                        routeProviders = null;
                    }

                    if (routeProviders != null && routeProviders.Count > 0)
                    {
                        AddRouteEntries(routes, actionName, routePrefix, routeProviders,
                                        new ReflectedHttpActionDescriptor[] { actionDescriptor });
                    }
                    else
                    {
                        // IF there are no routes on the specific action, attach it to the controller routes (if any).
                        actionsWithoutRoutes.Add(actionDescriptor);
                    }
                }
            }

            Collection <IHttpRouteInfoProvider> controllerRouteProviders =
                controllerDescriptor.GetCustomAttributes <IHttpRouteInfoProvider>(inherit: false);

            // If they exist and have not been overridden, create routes for controller-level route providers.
            if (controllerRouteProviders != null && controllerRouteProviders.Count > 0 &&
                actionsWithoutRoutes.Count > 0)
            {
                AddRouteEntries(routes, actionsWithoutRoutes[0].ActionName, routePrefix, controllerRouteProviders,
                                actionsWithoutRoutes);
            }

            return(routes);
        }
        static void ApplyImplicitConventions(HttpControllerDescriptor controller, IHttpActionSelector actionSelector, ApiVersionModel implicitVersionModel)
        {
            controller.SetApiVersionModel(implicitVersionModel);

            var actions = actionSelector.GetActionMapping(controller).SelectMany(g => g);

            foreach (var action in actions)
            {
                action.SetProperty(implicitVersionModel);
            }
        }
示例#8
0
        void ExploreRouteActions(
            IHttpRoute route,
            HttpControllerDescriptor controllerDescriptor,
            IHttpActionSelector actionSelector,
            Collection <VersionedApiDescription> apiDescriptions,
            ApiVersion apiVersion)
        {
            Contract.Requires(route != null);
            Contract.Requires(controllerDescriptor != null);
            Contract.Requires(actionSelector != null);
            Contract.Requires(apiDescriptions != null);
            Contract.Requires(apiVersion != null);

            var actionMapping = actionSelector.GetActionMapping(controllerDescriptor);

            if (actionMapping == null)
            {
                return;
            }

            const string ActionRouteParameterName = null;

            foreach (var grouping in actionMapping)
            {
                foreach (var action in grouping)
                {
                    if (!ShouldExploreAction(ActionRouteParameterName, action, route, apiVersion))
                    {
                        continue;
                    }

                    var parameterDescriptions = CreateParameterDescriptions(action, route);
                    var context = new ODataRouteBuilderContext(
                        Configuration,
                        apiVersion,
                        (ODataRoute)route,
                        action,
                        parameterDescriptions,
                        ModelTypeBuilder,
                        Options);

                    if (context.IsRouteExcluded)
                    {
                        continue;
                    }

                    var relativePath = new ODataRouteBuilder(context).Build();

                    PopulateActionDescriptions(action, route, context, relativePath, apiDescriptions, apiVersion);
                }
            }
        }
示例#9
0
        /// <summary>
        /// Returns a map, keyed by action string, of all <see cref="T:System.Web.Http.Controllers.HttpActionDescriptor" /> that the selector can select.  This is primarily called by <see cref="T:System.Web.Http.Description.IApiExplorer" /> to discover all the possible actions in the controller.
        /// </summary>
        /// <param name="controllerDescriptor">The controller descriptor.</param>
        /// <returns>
        /// A map of <see cref="T:System.Web.Http.Controllers.HttpActionDescriptor" /> that the selector can select, or null if the selector does not have a well-defined mapping of <see cref="T:System.Web.Http.Controllers.HttpActionDescriptor" />.
        /// </returns>
        /// <exception cref="System.NotImplementedException"></exception>
        public ILookup <string, HttpActionDescriptor> GetActionMapping(HttpControllerDescriptor controllerDescriptor)
        {
            var innerMapping = _innerSelector.GetActionMapping(controllerDescriptor);

            if (innerMapping.Contains(GenericNavigationPropertyRoutingConvention.PostNavigationPropertyMethodName))
            {
                // TODO: Cache per controller type - this is probably pretty expensive.
                return(ExpandGenericNavigationPropertyActions(innerMapping));
            }
            else
            {
                return(innerMapping);
            }
        }
        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);
            }
        }
示例#11
0
        static void Main(string[] args)
        {
            HttpConfiguration        configuration        = new HttpConfiguration();
            HttpControllerDescriptor controllerDescriptor = new HttpControllerDescriptor(configuration, "demo", typeof(DemoController));
            IHttpActionSelector      actionSelector       = configuration.Services.GetActionSelector();
            HttpActionDescriptor     actionDescriptor     = actionSelector.GetActionMapping(controllerDescriptor)["DemoAction"].First();
            IActionValueBinder       actionValueBinder    = configuration.Services.GetActionValueBinder();
            HttpActionBinding        actionBinding        = actionValueBinder.GetBinding(actionDescriptor);

            Console.WriteLine("{0,-18}{1}", "Parameter", "HttpParameterBinding");
            foreach (HttpParameterBinding parameterBinding in actionBinding.ParameterBindings)
            {
                Console.WriteLine("{0,-18}{1}", parameterBinding.Descriptor.ParameterName, parameterBinding.GetType().Name);
            }
        }
示例#12
0
        public IEnumerable <Tuple <string, string> > Get()
        {
            IHttpActionSelector actionSelector = GlobalConfiguration.Configuration.Services.GetActionSelector();

            foreach (var group in actionSelector.GetActionMapping(this.ControllerContext.ControllerDescriptor))
            {
                foreach (HttpActionDescriptor actionDescriptor in group)
                {
                    if (actionDescriptor.ActionName != "Get")
                    {
                        string converterTypeName = actionDescriptor.ResultConverter == null ? "N/A" : actionDescriptor.ResultConverter.GetType().Name;
                        yield return(new Tuple <string, string>(actionDescriptor.ActionName, converterTypeName));
                    }
                }
            }
        }
        /// <summary>
        /// Maps the attribute-defined routes for the application.
        /// </summary>
        /// <param name="configuration">The server configuration.</param>
        /// <param name="routeBuilder">The <see cref="HttpRouteBuilder"/> to use for generating attribute routes.</param>
        public static void MapHttpAttributeRoutes(this HttpConfiguration configuration, HttpRouteBuilder routeBuilder)
        {
            if (configuration == null)
            {
                throw Error.ArgumentNull("configuration");
            }

            if (routeBuilder == null)
            {
                throw Error.ArgumentNull("routeBuilder");
            }

            List <HttpRouteEntry> attributeRoutes = new List <HttpRouteEntry>();

            IHttpControllerSelector controllerSelector = configuration.Services.GetHttpControllerSelector();
            IDictionary <string, HttpControllerDescriptor> controllerMapping = controllerSelector.GetControllerMapping();

            if (controllerMapping != null)
            {
                foreach (HttpControllerDescriptor controllerDescriptor in controllerMapping.Values)
                {
                    Collection <RoutePrefixAttribute> routePrefixes = controllerDescriptor.GetCustomAttributes <RoutePrefixAttribute>(inherit: false);
                    IHttpActionSelector actionSelector = controllerDescriptor.Configuration.Services.GetActionSelector();
                    ILookup <string, HttpActionDescriptor> actionMapping = actionSelector.GetActionMapping(controllerDescriptor);
                    if (actionMapping != null)
                    {
                        foreach (IGrouping <string, HttpActionDescriptor> actionGrouping in actionMapping)
                        {
                            string controllerName = controllerDescriptor.ControllerName;
                            attributeRoutes.AddRange(CreateAttributeRoutes(routeBuilder, controllerName, routePrefixes, actionGrouping));
                        }
                    }
                }

                attributeRoutes.Sort();

                foreach (HttpRouteEntry attributeRoute in attributeRoutes)
                {
                    configuration.Routes.Add(attributeRoute.Name, attributeRoute.Route);
                }
            }
        }
示例#14
0
        static HttpControllerDescriptor[] ApplyCollatedModel(List <HttpControllerDescriptor> controllers, IHttpActionSelector actionSelector, ApiVersionModel collatedModel)
        {
            Contract.Requires(controllers != null);
            Contract.Requires(actionSelector != null);
            Contract.Requires(collatedModel != null);
            Contract.Ensures(Contract.Result <HttpControllerDescriptor[]>() != null);

            foreach (var controller in controllers)
            {
                var model   = controller.GetApiVersionModel();
                var actions = actionSelector.GetActionMapping(controller).SelectMany(g => g);

                controller.SetProperty(model.Aggregate(collatedModel));

                foreach (var action in actions)
                {
                    model = action.GetApiVersionModel();
                    action.SetProperty(model.Aggregate(collatedModel));
                }
            }

            return(controllers.ToArray());
        }
        static HttpControllerDescriptor[] ApplyCollatedModels(List <HttpControllerDescriptor> controllers, IHttpActionSelector actionSelector)
        {
            var supported          = new HashSet <ApiVersion>();
            var deprecated         = new HashSet <ApiVersion>();
            var controllerModels   = new List <ApiVersionModel>(controllers.Count);
            var actionModels       = new List <ApiVersionModel>(controllers.Count);
            var visitedControllers = new List <Tuple <HttpControllerDescriptor, ApiVersionModel> >(controllers.Count);
            var visitedActions     = new List <Tuple <HttpActionDescriptor, ApiVersionModel> >(controllers.Count);

            for (var i = 0; i < controllers.Count; i++)
            {
                // 1 - collate controller versions
                var controller = controllers[i];
                var model      = controller.GetApiVersionModel();

                if (model.IsApiVersionNeutral)
                {
                    continue;
                }

                controllerModels.Add(model);
                visitedControllers.Add(Tuple.Create(controller, model));

                // 2 - collate action versions
                var actions = actionSelector.GetActionMapping(controller).SelectMany(g => g);

                foreach (var action in actions)
                {
                    model = action.GetApiVersionModel();

                    if (model.IsApiVersionNeutral)
                    {
                        continue;
                    }

                    actionModels.Add(model);
                    visitedActions.Add(Tuple.Create(action, model));
                }
            }

            // 3 - apply collated action model
            var collatedModel = actionModels.Aggregate();

            for (var i = 0; i < visitedActions.Count; i++)
            {
                var(action, model) = visitedActions[i];

                action.SetProperty(model.Aggregate(collatedModel));
            }

            // 4 - apply collated controller model
            // note: allows controllers to report versions in 400s even when an action is unmatched
            controllerModels.Add(collatedModel);
            collatedModel = controllerModels.Aggregate();

            for (var i = 0; i < visitedControllers.Count; i++)
            {
                var(controller, model) = visitedControllers[i];

                controller.SetApiVersionModel(model.Aggregate(collatedModel));
            }

            return(controllers.ToArray());
        }
示例#16
0
 public ILookup <string, HttpActionDescriptor> GetActionMapping(HttpControllerDescriptor controllerDescriptor)
 {
     return(_innerSelector.GetActionMapping(controllerDescriptor));
 }
示例#17
0
 public System.Linq.ILookup <string, HttpActionDescriptor> GetActionMapping(HttpControllerDescriptor controllerDescriptor)
 {
     return(_concreteSelector.GetActionMapping(controllerDescriptor));
 }
        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);
                }
            }
        }
示例#19
0
        public IEnumerable <Tuple <string, string, FilterScope> > Get()
        {
            HttpControllerDescriptor[] controllerDescriptors = new HttpControllerDescriptor[] {
                new HttpControllerDescriptor(this.Configuration, "foo", typeof(FooController)),
                new HttpControllerDescriptor(this.Configuration, "bar", typeof(BarController)),
                new HttpControllerDescriptor(this.Configuration, "baz", typeof(BazController)),
            };

            IHttpActionSelector actionSelector = this.Configuration.Services.GetActionSelector();
            IEnumerable <HttpActionDescriptor> actionDescriptors = controllerDescriptors.SelectMany(controllerDescriptor =>
                                                                                                    actionSelector.GetActionMapping(controllerDescriptor)["Action"]);

            foreach (HttpActionDescriptor actionDescriptor in actionDescriptors)
            {
                foreach (FilterInfo filterInfo in actionDescriptor.GetFilterPipeline())
                {
                    yield return(new Tuple <string, string, FilterScope>(
                                     string.Format("{0}.{1}", actionDescriptor.ControllerDescriptor.ControllerType.Name, actionDescriptor.ActionName),
                                     filterInfo.Instance.GetType().Name,
                                     filterInfo.Scope));
                }
            }
        }
        private static IEnumerable <HttpRouteEntry> CreateRouteEntries(HttpControllerDescriptor controllerDescriptor)
        {
            IHttpActionSelector actionSelector = controllerDescriptor.Configuration.Services.GetActionSelector();
            ILookup <string, HttpActionDescriptor> actionMap = actionSelector.GetActionMapping(controllerDescriptor);

            if (actionMap == null)
            {
                return(Enumerable.Empty <HttpRouteEntry>());
            }

            List <HttpRouteEntry> routes = new List <HttpRouteEntry>();
            string routePrefix           = GetRoutePrefix(controllerDescriptor);

            foreach (IGrouping <string, HttpActionDescriptor> actionGrouping in actionMap)
            {
                string actionName = actionGrouping.Key;

                foreach (ReflectedHttpActionDescriptor actionDescriptor in actionGrouping.OfType <ReflectedHttpActionDescriptor>())
                {
                    foreach (IHttpRouteInfoProvider routeProvider in actionDescriptor.GetCustomAttributes <IHttpRouteInfoProvider>(inherit: false))
                    {
                        string providerTemplate = routeProvider.RouteTemplate;
                        if (providerTemplate == null)
                        {
                            continue;
                        }

                        if (providerTemplate.StartsWith("/", StringComparison.Ordinal))
                        {
                            throw Error.InvalidOperation(SRResources.AttributeRoutes_InvalidTemplate, providerTemplate, actionName);
                        }

                        string routeTemplate = BuildRouteTemplate(routePrefix, providerTemplate);

                        // Try to find an entry with the same route template and the same HTTP verbs
                        HttpRouteEntry existingEntry = null;
                        foreach (HttpRouteEntry entry in routes)
                        {
                            if (String.Equals(routeTemplate, entry.RouteTemplate, StringComparison.OrdinalIgnoreCase) &&
                                AreEqual(routeProvider.HttpMethods, entry.HttpMethods))
                            {
                                existingEntry = entry;
                                break;
                            }
                        }

                        if (existingEntry == null)
                        {
                            HttpRouteEntry entry = new HttpRouteEntry()
                            {
                                RouteTemplate = routeTemplate,
                                Actions       = new HashSet <ReflectedHttpActionDescriptor>()
                                {
                                    actionDescriptor
                                }
                            };

                            entry.HttpMethods = routeProvider.HttpMethods;
                            entry.Name        = routeProvider.RouteName;
                            entry.Order       = routeProvider.RouteOrder;
                            routes.Add(entry);
                        }
                        else
                        {
                            existingEntry.Actions.Add(actionDescriptor);

                            // Take the minimum of the two orders as the order
                            int order = routeProvider.RouteOrder;
                            if (order < existingEntry.Order)
                            {
                                existingEntry.Order = order;
                            }

                            // Use the provider route name if the route hasn't already been named
                            if (existingEntry.Name == null)
                            {
                                existingEntry.Name = routeProvider.RouteName;
                            }
                        }
                    }
                }
            }

            return(routes);
        }
        void ExploreRouteActions(
            IHttpRoute route,
            HttpControllerDescriptor controllerDescriptor,
            IHttpActionSelector actionSelector,
            Collection <VersionedApiDescription> apiDescriptions,
            ApiVersion apiVersion)
        {
            var actionMapping = actionSelector.GetActionMapping(controllerDescriptor);

            if (actionMapping == null)
            {
                return;
            }

            foreach (var grouping in actionMapping)
            {
                foreach (var action in grouping)
                {
                    if (!ShouldExploreAction(actionRouteParameterValue: string.Empty, action, route, apiVersion))
                    {
                        continue;
                    }

                    var parameterDescriptions = CreateParameterDescriptions(action, route, apiVersion);
                    var context = new ODataRouteBuilderContext(
                        Configuration,
                        apiVersion,
                        (ODataRoute)route,
                        action,
                        parameterDescriptions,
                        ModelTypeBuilder,
                        Options);

                    if (context.IsRouteExcluded)
                    {
                        continue;
                    }

                    var routeBuilder = new ODataRouteBuilder(context);
                    var relativePath = routeBuilder.Build();

                    if (routeBuilder.IsNavigationPropertyLink)
                    {
                        var routeTemplates = routeBuilder.ExpandNavigationPropertyLinkTemplate(relativePath);
                        var afterPrefix    = string.IsNullOrEmpty(context.RoutePrefix) ? 0 : context.RoutePrefix !.Length + 1;

                        for (var i = 0; i < routeTemplates.Count; i++)
                        {
                            relativePath = routeTemplates[i];

                            var queryParamAdded = false;

                            if (action.ActionName.StartsWith("DeleteRef", Ordinal))
                            {
                                var handler      = context.PathTemplateHandler;
                                var pathTemplate = handler.ParseTemplate(relativePath.Substring(afterPrefix), context.Services);
                                var template     = pathTemplate?.Segments.OfType <NavigationPropertyLinkSegmentTemplate>().FirstOrDefault();

                                if (template != null)
                                {
                                    var property = template.Segment.NavigationProperty;

                                    if (property.TargetMultiplicity() == EdmMultiplicity.Many)
                                    {
                                        routeBuilder.AddOrReplaceRefIdQueryParameter();
                                        queryParamAdded = true;
                                    }
                                }
                            }

                            PopulateActionDescriptions(action, route, context, relativePath, apiDescriptions, apiVersion);

                            if (queryParamAdded)
                            {
                                for (var j = 0; j < context.ParameterDescriptions.Count; j++)
                                {
                                    var parameter = context.ParameterDescriptions[j];

                                    if (parameter.Name == "$id" || parameter.Name == "id")
                                    {
                                        context.ParameterDescriptions.RemoveAt(j);
                                        break;
                                    }
                                }
                            }
                        }
                    }
                    else
                    {
                        PopulateActionDescriptions(action, route, context, relativePath, apiDescriptions, apiVersion);
                    }
                }
            }
        }