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); }
/// <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); } }
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); } } }
/// <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); } }
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); } }
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); } } }
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()); }
public ILookup <string, HttpActionDescriptor> GetActionMapping(HttpControllerDescriptor controllerDescriptor) { return(_innerSelector.GetActionMapping(controllerDescriptor)); }
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); } } }
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); } } } }