Ejemplo n.º 1
0
        public void ConfigOperationOptions_ThrowsODataException()
        {
            // Arrange
            string expect = "The route option disables qualified and unqualified operation call. At least one option should enable.";

            // Act & Assert
            Action test = () =>
            {
                ODataRouteOptions options = new ODataRouteOptions();
                options.EnableQualifiedOperationCall   = false;
                options.EnableUnqualifiedOperationCall = false;
            };

            ExceptionAssert.Throws <ODataException>(test, expect);

            // Act & Assert
            test = () =>
            {
                ODataRouteOptions options = new ODataRouteOptions();
                options.EnableUnqualifiedOperationCall = false;
                options.EnableQualifiedOperationCall   = false;
            };

            ExceptionAssert.Throws <ODataException>(test, expect);
        }
Ejemplo n.º 2
0
        /// <inheritdoc />
        public override IEnumerable <string> GetTemplates(ODataRouteOptions options)
        {
            if (RelatedKey != null)
            {
                options = options ?? ODataRouteOptions.Default;
                Contract.Assert(options.EnableKeyInParenthesis || options.EnableKeyAsSegment);

                if (options.EnableKeyInParenthesis && options.EnableKeyAsSegment)
                {
                    yield return($"/{{{ParameterName}}}({{{RelatedKey}}})/$ref");

                    yield return($"/{{{ParameterName}}}/{{{RelatedKey}}}/$ref");
                }
                else if (options.EnableKeyInParenthesis)
                {
                    yield return($"/{{{ParameterName}}}({{{RelatedKey}}})/$ref");
                }
                else
                {
                    yield return($"/{{{ParameterName}}}/{{{RelatedKey}}}/$ref");
                }
            }
            else
            {
                yield return($"/{{{ParameterName}}}/$ref");
            }
        }
Ejemplo n.º 3
0
        public void ConfigKeyOptions_ThrowsODataException()
        {
            // Arrange
            string expect = "The route option disables key in parenthesis and key as segment. At least one option should enable.";

            // Act & Assert
            Action test = () =>
            {
                ODataRouteOptions options = new ODataRouteOptions();
                options.EnableKeyAsSegment     = false;
                options.EnableKeyInParenthesis = false;
            };

            ExceptionAssert.Throws <ODataException>(test, expect);

            // Act & Assert
            test = () =>
            {
                ODataRouteOptions options = new ODataRouteOptions();
                options.EnableKeyInParenthesis = false;
                options.EnableKeyAsSegment     = false;
            };

            ExceptionAssert.Throws <ODataException>(test, expect);
        }
Ejemplo n.º 4
0
        public void CtorODataRouteOptions_HasDefaultProperties()
        {
            // Arrange & Act
            ODataRouteOptions options = new ODataRouteOptions();

            // Assert
            Assert.True(options.EnableKeyInParenthesis);
            Assert.True(options.EnableKeyAsSegment);
            Assert.True(options.EnableQualifiedOperationCall);
            Assert.True(options.EnableUnqualifiedOperationCall);
            Assert.False(options.EnableNonParenthsisForEmptyParameterFunction);
        }
Ejemplo n.º 5
0
        /// <summary>
        /// Generates all templates for the given <see cref="ODataPathTemplate"/> using the given <see cref="ODataRouteOptions"/>.
        /// All templates mean:
        /// 1) for key segment, we have key in parenthesis and key as segment.
        /// 2) for bound function/action segment, we have qualified function call and unqualified function call.
        /// All of such might be based on route options.
        /// </summary>
        /// <param name="options">The route options.</param>
        /// <returns>All path templates.</returns>
        public virtual IEnumerable <string> GetTemplates(ODataRouteOptions options = null)
        {
            options = options ?? ODataRouteOptions.Default;

            Stack <string> stack = new Stack <string>();

            IList <string> templates = new List <string>();

            ProcessSegment(stack, 0, Count, templates, options);

            return(templates);
        }
Ejemplo n.º 6
0
        private static void Verify(Func <ODataRouteOptions, bool> func, Action <ODataRouteOptions, bool> config, bool defValue = true)
        {
            // Arrange
            ODataRouteOptions options = new ODataRouteOptions();

            Assert.Equal(defValue, func(options));

            // Act
            config(options, !defValue);

            // Assert
            Assert.Equal(!defValue, func(options));
        }
        public void DefaultODataRouteOptions_HasDefaultProperties()
        {
            // Arrange & Act
            ODataRouteOptions options = ODataRouteOptions.Default;

            // Assert
            Assert.True(options.EnableKeyInParenthesis);
            Assert.True(options.EnableKeyAsSegment);
            Assert.True(options.EnableQualifiedOperationCall);
            Assert.True(options.EnableUnqualifiedOperationCall);
            Assert.False(options.EnableNonParenthsisForEmptyParameterFunction);
            Assert.False(options.EnableControllerNameCaseInsensitive);
        }
Ejemplo n.º 8
0
        public void ConfigOperationOptions_DoesNotThrowsODataException()
        {
            // Arrange & Act & Assert
            ODataRouteOptions options = new ODataRouteOptions();

            options.EnableQualifiedOperationCall   = false;
            options.EnableUnqualifiedOperationCall = true;

            // Arrange & Act & Assert
            options = new ODataRouteOptions();
            options.EnableUnqualifiedOperationCall = false;
            options.EnableQualifiedOperationCall   = true;
        }
Ejemplo n.º 9
0
        public void ConfigKeyOptions_DoesNotThrowsODataException()
        {
            // Arrange & Act & Assert
            ODataRouteOptions options = new ODataRouteOptions();

            options.EnableKeyAsSegment     = false;
            options.EnableKeyInParenthesis = true;

            // Arrange & Act & Assert
            options = new ODataRouteOptions();
            options.EnableKeyInParenthesis = false;
            options.EnableKeyAsSegment     = true;
        }
Ejemplo n.º 10
0
        /// <inheritdoc />
        public override IEnumerable <string> GetTemplates(ODataRouteOptions options)
        {
            options = options ?? ODataRouteOptions.Default;

            if (ParameterMappings.Count == 0 && options.EnableNonParenthsisForEmptyParameterFunction)
            {
                yield return($"/{FunctionImport.Name}");
            }
            else
            {
                string parameters = string.Join(",", ParameterMappings.Select(a => $"{a.Key}={{{a.Value}}}"));
                yield return($"/{FunctionImport.Name}({parameters})");
            }
        }
 /// <inheritdoc />
 public override IEnumerable <string> GetTemplates(ODataRouteOptions options)
 {
     if (Key != null)
     {
         IEnumerable <string> keyTemplates = Key.GetTemplates(options);
         foreach (var keyTemplate in keyTemplates)
         {
             yield return($"/{NavigationProperty.Name}{keyTemplate}/$ref");
         }
     }
     else
     {
         yield return($"/{NavigationProperty.Name}/$ref");
     }
 }
Ejemplo n.º 12
0
        /// <inheritdoc />
        public override IEnumerable <string> GetTemplates(ODataRouteOptions options)
        {
            options = options ?? ODataRouteOptions.Default;
            Contract.Assert(options.EnableQualifiedOperationCall || options.EnableUnqualifiedOperationCall);

            if (options.EnableQualifiedOperationCall && options.EnableUnqualifiedOperationCall)
            {
                yield return($"/{Action.FullName()}");

                yield return($"/{Action.Name}");
            }
            else if (options.EnableQualifiedOperationCall)
            {
                yield return($"/{Action.FullName()}");
            }
            else
            {
                yield return($"/{Action.Name}");
            }
        }
Ejemplo n.º 13
0
        /// <inheritdoc />
        public override IEnumerable <string> GetTemplates(ODataRouteOptions options)
        {
            options = options ?? ODataRouteOptions.Default;

            Contract.Assert(options.EnableKeyInParenthesis || options.EnableKeyAsSegment);

            if (options.EnableKeyInParenthesis && options.EnableKeyAsSegment)
            {
                yield return($"({_keyLiteral})");

                yield return($"/{_keyLiteral}");
            }
            else if (options.EnableKeyInParenthesis)
            {
                yield return($"({_keyLiteral})");
            }
            else if (options.EnableKeyAsSegment)
            {
                yield return($"/{_keyLiteral}");
            }
        }
        /// <inheritdoc />
        public override IEnumerable <string> GetTemplates(ODataRouteOptions options)
        {
            options = options ?? ODataRouteOptions.Default;
            Contract.Assert(options.EnableQualifiedOperationCall || options.EnableUnqualifiedOperationCall);

            string unqualifiedIdentifier, qualifiedIdentifier;

            if (ParameterMappings.Count == 0 && options.EnableNonParenthsisForEmptyParameterFunction)
            {
                unqualifiedIdentifier = "/" + Function.Name;
                qualifiedIdentifier   = "/" + Function.FullName();
            }
            else
            {
                string parameterStr = "(" + string.Join(",", ParameterMappings.Select(a => $"{a.Key}={{{a.Value}}}")) + ")";
                unqualifiedIdentifier = "/" + Function.Name + parameterStr;
                qualifiedIdentifier   = "/" + Function.FullName() + parameterStr;
            }

            if (options.EnableQualifiedOperationCall && options.EnableUnqualifiedOperationCall)
            {
                // "/NS.Function(...)"
                yield return(qualifiedIdentifier);

                // "/Function(...)"
                yield return(unqualifiedIdentifier);
            }
            else if (options.EnableQualifiedOperationCall)
            {
                // "/NS.Function(...)"
                yield return(qualifiedIdentifier);
            }
            else
            {
                // "/Function(...)"
                yield return(unqualifiedIdentifier);
            }
        }
Ejemplo n.º 15
0
        private void ProcessSegment(Stack <string> stack, int index, int count, IList <string> templates, ODataRouteOptions options)
        {
            if (index == count)
            {
                string pathTemplate = string.Join("", stack.Reverse());
                if (pathTemplate.StartsWith('/'))
                {
                    pathTemplate = pathTemplate.Substring(1); // remove the first "/"
                }

                templates.Add(pathTemplate);
                return;
            }

            ODataSegmentTemplate segment = this[index];

            foreach (string template in segment.GetTemplates(options))
            {
                stack.Push(template);

                ProcessSegment(stack, index + 1, count, templates, options);

                stack.Pop();
            }
        }
Ejemplo n.º 16
0
 /// <inheritdoc />
 public override IEnumerable <string> GetTemplates(ODataRouteOptions options)
 {
     yield return($"/{Segment.Singleton.Name}");
 }
Ejemplo n.º 17
0
 /// <inheritdoc />
 public override IEnumerable <string> GetTemplates(ODataRouteOptions options)
 {
     yield return("/$value");
 }
Ejemplo n.º 18
0
        /// <summary>
        /// Adds the OData selector model to the action.
        /// </summary>
        /// <param name="action">The given action model.</param>
        /// <param name="httpMethods">The supported http methods, if multiple, using ',' to separate.</param>
        /// <param name="prefix">The prefix.</param>
        /// <param name="model">The Edm model.</param>
        /// <param name="path">The OData path template.</param>
        /// <param name="options">The route build options.</param>
        public static void AddSelector(this ActionModel action, string httpMethods, string prefix, IEdmModel model, ODataPathTemplate path, ODataRouteOptions options = null)
        {
            if (action == null)
            {
                throw Error.ArgumentNull(nameof(action));
            }

            if (string.IsNullOrEmpty(httpMethods))
            {
                throw Error.ArgumentNullOrEmpty(nameof(httpMethods));
            }

            if (model == null)
            {
                throw Error.ArgumentNull(nameof(model));
            }

            if (path == null)
            {
                throw Error.ArgumentNull(nameof(path));
            }

            // if the controller has attribute route decorated, for example:
            // [Route("api/[controller]")]
            // public class CustomersController : Controller
            // {}
            // let's always create new selector model for action.
            // Since the new created selector model is absolute attribute route, the controller attribute route doesn't apply to this selector model.
            bool hasAttributeRouteOnController = action.Controller.Selectors.Any(s => s.AttributeRouteModel != null);

            // If the methods have different case sensitive, for example, "get", "Get", in the ASP.NET Core 3.1,
            // It will throw "An item with the same key has already been added. Key: GET", in
            // HttpMethodMatcherPolicy.BuildJumpTable(Int32 exitDestination, IReadOnlyList`1 edges)
            // Another root cause is that in attribute routing, we reuse the HttpMethodMetadata, the method name is always "upper" case.
            // Therefore, we upper the http method name always.
            string[] methods = httpMethods.ToUpperInvariant().Split(',');
            foreach (string template in path.GetTemplates(options))
            {
                // Be noted: https://github.com/dotnet/aspnetcore/blob/main/src/Mvc/Mvc.Core/src/ApplicationModels/ActionAttributeRouteModel.cs#L74-L75
                // No matter whether the action selector model is absolute route template, the controller's attribute will apply automatically
                // So, let's only create/update the action selector model
                SelectorModel selectorModel = action.Selectors.FirstOrDefault(s => s.AttributeRouteModel == null);
                if (hasAttributeRouteOnController || selectorModel == null)
                {
                    // Create a new selector model.
                    selectorModel = CreateSelectorModel(action, methods);
                    action.Selectors.Add(selectorModel);
                }
                else
                {
                    // Update the existing non attribute routing selector model.
                    selectorModel = UpdateSelectorModel(selectorModel, methods);
                }

                ODataRoutingMetadata odataMetadata = new ODataRoutingMetadata(prefix, model, path);
                selectorModel.EndpointMetadata.Add(odataMetadata);

                string templateStr = string.IsNullOrEmpty(prefix) ? template : $"{prefix}/{template}";

                selectorModel.AttributeRouteModel = new AttributeRouteModel
                {
                    // OData convention route template doesn't get combined with the route template applied to the controller.
                    // Route templates applied to an action that begin with / or ~/ don't get combined with route templates applied to the controller.
                    Template = $"/{templateStr}",
                    Name     = templateStr // do we need this?
                };

                // Check with .NET Team whether the "Endpoint name metadata" needed?
                selectorModel.EndpointMetadata.Add(new EndpointNameMetadata(Guid.NewGuid().ToString())); // Do we need this?
            }
        }
Ejemplo n.º 19
0
 public override IEnumerable <string> GetTemplates(ODataRouteOptions options)
 {
     yield return("/{classname}");
 }
Ejemplo n.º 20
0
 /// <inheritdoc />
 public override IEnumerable <string> GetTemplates(ODataRouteOptions options)
 {
     yield return($"/{Segment.LiteralText}");
 }
        /// <summary>
        /// Adds the OData selector model to the action.
        /// </summary>
        /// <param name="action">The given action model.</param>
        /// <param name="httpMethod">The supported http methods, if mulitple, using ',' to separate.</param>
        /// <param name="prefix">The prefix.</param>
        /// <param name="model">The Edm model.</param>
        /// <param name="path">The OData path template.</param>
        /// <param name="options">The route build options.</param>
        public static void AddSelector(this ActionModel action, string httpMethod, string prefix, IEdmModel model, ODataPathTemplate path, ODataRouteOptions options = null)
        {
            if (action == null)
            {
                throw Error.ArgumentNull(nameof(action));
            }

            if (model == null)
            {
                throw Error.ArgumentNull(nameof(model));
            }

            if (path == null)
            {
                throw Error.ArgumentNull(nameof(path));
            }

            foreach (string template in path.GetTemplates(options))
            {
                // We have to check the selector model on controller?
                SelectorModel selectorModel = action.Selectors.FirstOrDefault(s => s.AttributeRouteModel == null);
                if (selectorModel == null)
                {
                    selectorModel = CreateSelectorModel(action.Attributes);
                    action.Selectors.Add(selectorModel);
                }

                ODataRoutingMetadata odataMetadata = new ODataRoutingMetadata(prefix, model, path);
                AddHttpMethod(odataMetadata, httpMethod);

                selectorModel.EndpointMetadata.Add(odataMetadata);

                string templateStr = string.IsNullOrEmpty(prefix) ? template : $"{prefix}/{template}";

                // OData convention route template doesn't get combined with the route template applied to the controller.
                // Route templates applied to an action that begin with / or ~/ don't get combined with route templates applied to the controller.
                templateStr = "/" + templateStr;

                selectorModel.AttributeRouteModel = new AttributeRouteModel(new RouteAttribute(templateStr)
                {
                    Name = templateStr
                });

                // Check with .NET Team whether the "Endpoint name metadata"
                selectorModel.EndpointMetadata.Add(new EndpointNameMetadata(Guid.NewGuid().ToString()));
            }
        }
Ejemplo n.º 22
0
 /// <summary>
 /// Gets the templates. It's case-insensitive template.
 /// It's used to build the routing template in conventional routing.
 /// It's not used in attribute routing.
 /// The template string should include the leading "/" if apply.
 /// </summary>
 /// <param name="options">The route options.</param>
 /// <returns>The built segment templates.</returns>
 public abstract IEnumerable <string> GetTemplates(ODataRouteOptions options);
        /// <summary>
        /// Generates all templates for the given <see cref="ODataPathTemplate"/>.
        /// All templates mean:
        /// 1) for key segment, we have key in parenthesis and key as segment.
        /// 2) for bound function segment, we have qualified function call and unqualified function call.
        /// </summary>
        /// <param name="path">The given path template.</param>
        /// <param name="options">The route options.</param>
        /// <returns>All path templates.</returns>
        public static IEnumerable <string> GetTemplates(this ODataPathTemplate path, ODataRouteOptions options = null)
        {
            if (path == null)
            {
                throw Error.ArgumentNull(nameof(path));
            }

            options = options ?? ODataRouteOptions.Default;

            IList <StringBuilder> templates = new List <StringBuilder>
            {
                new StringBuilder()
            };

            int count = path.Segments.Count;

            for (int index = 0; index < count; index++)
            {
                ODataSegmentTemplate segment = path.Segments[index];

                if (segment.Kind == ODataSegmentKind.Key)
                {
                    // for key segment, if it's single key, let's add key as segment template also
                    // otherwise, we only add the key in parenthesis template.
                    KeySegmentTemplate keySg = segment as KeySegmentTemplate;
                    templates = AppendKeyTemplate(templates, keySg, options);
                    continue;
                }

                if (index != 0)
                {
                    templates = CombinateTemplate(templates, "/");
                }

                // create =>  ~.../navigation/{key}/$ref
                if (segment.Kind == ODataSegmentKind.NavigationLink)
                {
                    NavigationLinkSegmentTemplate navigationLinkSegment = (NavigationLinkSegmentTemplate)segment;
                    if (index == count - 1)
                    {
                        // we don't have the other segment
                        templates = CombinateTemplates(templates, $"{navigationLinkSegment.Segment.NavigationProperty.Name}/$ref");
                    }
                    else
                    {
                        ODataSegmentTemplate nextSegment = path.Segments[index + 1];
                        if (nextSegment.Kind == ODataSegmentKind.Key)
                        {
                            // append "navigation property"
                            templates = CombinateTemplates(templates, navigationLinkSegment.Segment.NavigationProperty.Name);

                            // append "key"
                            KeySegmentTemplate keySg = nextSegment as KeySegmentTemplate;
                            templates = AppendKeyTemplate(templates, keySg, options);

                            // append $ref
                            templates = CombinateTemplates(templates, "/$ref");
                            index++; // skip the key segment after $ref.
                        }
                        else
                        {
                            templates = CombinateTemplates(templates, $"{navigationLinkSegment.Segment.NavigationProperty.Name}/$ref");
                        }
                    }

                    continue;
                }

                if (segment.Kind == ODataSegmentKind.Action)
                {
                    ActionSegmentTemplate action = (ActionSegmentTemplate)segment;
                    templates = AppendActionTemplate(templates, action, options);
                }
                else if (segment.Kind == ODataSegmentKind.Function)
                {
                    FunctionSegmentTemplate function = (FunctionSegmentTemplate)segment;
                    templates = AppendFunctionTemplate(templates, function, options);
                }
                else
                {
                    templates = CombinateTemplate(templates, segment.Literal);
                }
            }

            return(templates.Select(t => t.ToString()));
        }
        private static IList <StringBuilder> AppendFunctionTemplate(IList <StringBuilder> templates, FunctionSegmentTemplate segment, ODataRouteOptions options)
        {
            Contract.Assert(segment != null);
            Contract.Assert(options != null);

            if (options.EnableQualifiedOperationCall && options.EnableUnqualifiedOperationCall)
            {
                return(CombinateTemplates(templates, segment.Literal, segment.UnqualifiedIdentifier));
            }
            else if (options.EnableQualifiedOperationCall)
            {
                return(CombinateTemplate(templates, segment.Literal));
            }
            else if (options.EnableUnqualifiedOperationCall)
            {
                return(CombinateTemplate(templates, segment.UnqualifiedIdentifier));
            }
            else
            {
                throw new ODataException(Error.Format(SRResources.RouteOptionDisabledOperationSegment, "function"));
            }
        }
        private static IList <StringBuilder> AppendKeyTemplate(IList <StringBuilder> templates, KeySegmentTemplate segment, ODataRouteOptions options)
        {
            Contract.Assert(segment != null);
            Contract.Assert(options != null);

            if (options.EnableKeyInParenthesis && options.EnableKeyAsSegment)
            {
                return(CombinateTemplates(templates, $"({segment.Literal})", $"/{segment.Literal}"));
            }
            else if (options.EnableKeyInParenthesis)
            {
                return(CombinateTemplate(templates, $"({segment.Literal})"));
            }
            else if (options.EnableKeyAsSegment)
            {
                return(CombinateTemplate(templates, $"/{segment.Literal}"));
            }
            else
            {
                throw new ODataException(SRResources.RouteOptionDisabledKeySegment);
            }
        }
        private static void AddSelector(IEdmEntitySet entitySet, IEdmEntityType entityType,
                                        IEdmStructuredType castType, string prefix, IEdmModel model, ActionModel action, string httpMethod, ODataRouteOptions options)
        {
            IList <ODataSegmentTemplate> segments = new List <ODataSegmentTemplate>
            {
                new EntitySetSegmentTemplate(entitySet),
                KeySegmentTemplate.CreateKeySegment(entityType, entitySet)
            };

            // If we have the type cast
            if (castType != null)
            {
                if (castType == entityType)
                {
                    // If cast type is the entity type of the entity set.
                    // we support two templates
                    // ~/Customers({key})
                    action.AddSelector(httpMethod, prefix, model, new ODataPathTemplate(segments), options);

                    // ~/Customers({key})/Ns.Customer
                    segments.Add(new CastSegmentTemplate(castType, entityType, entitySet));
                    action.AddSelector(httpMethod, prefix, model, new ODataPathTemplate(segments), options);
                }
                else
                {
                    // ~/Customers({key})/Ns.VipCustomer
                    segments.Add(new CastSegmentTemplate(castType, entityType, entitySet));
                    action.AddSelector(httpMethod, prefix, model, new ODataPathTemplate(segments), options);
                }
            }
            else
            {
                // ~/Customers({key})
                action.AddSelector(httpMethod, prefix, model, new ODataPathTemplate(segments), options);
            }
        }
Ejemplo n.º 27
0
 /// <inheritdoc />
 public override IEnumerable <string> GetTemplates(ODataRouteOptions options)
 {
     yield return($"/{Property.Name}");
 }
        private static IList <TemplateInfo> AppendActionTemplate(IList <TemplateInfo> templates, ActionSegmentTemplate segment, ODataRouteOptions options)
        {
            Contract.Assert(segment != null);
            Contract.Assert(options != null);

            string fullName = segment.Action.FullName();
            string name     = segment.Action.Name;

            if (options.EnableQualifiedOperationCall && options.EnableUnqualifiedOperationCall)
            {
                return(CombinateTemplates(templates, (fullName, fullName), (name, name)));
            }
            else if (options.EnableQualifiedOperationCall)
            {
                return(CombinateTemplate(templates, (fullName, fullName)));
            }
            else if (options.EnableUnqualifiedOperationCall)
            {
                return(CombinateTemplate(templates, (name, name)));
            }
            else
            {
                throw new ODataException(Error.Format(SRResources.RouteOptionDisabledOperationSegment, "action"));
            }
        }
Ejemplo n.º 29
0
        /// <summary>
        /// Adds the OData selector model to the action.
        /// </summary>
        /// <param name="action">The given action model.</param>
        /// <param name="httpMethod">The supported http methods, if mulitple, using ',' to separate.</param>
        /// <param name="prefix">The prefix.</param>
        /// <param name="model">The Edm model.</param>
        /// <param name="path">The OData path template.</param>
        /// <param name="options">The route build options.</param>
        public static void AddSelector(this ActionModel action, string httpMethod, string prefix, IEdmModel model, ODataPathTemplate path, ODataRouteOptions options = null)
        {
            if (action == null)
            {
                throw Error.ArgumentNull(nameof(action));
            }

            if (model == null)
            {
                throw Error.ArgumentNull(nameof(model));
            }

            if (path == null)
            {
                throw Error.ArgumentNull(nameof(path));
            }

            ODataRoutingMetadata odataMetadata = new ODataRoutingMetadata(prefix, model, path);

            AddHttpMethod(odataMetadata, httpMethod);

            foreach (string template in path.GetTemplates(options))
            {
                SelectorModel selectorModel = action.Selectors.FirstOrDefault(s => s.AttributeRouteModel == null);
                if (selectorModel == null)
                {
                    selectorModel = CreateSelectorModel(action.Attributes);
                    action.Selectors.Add(selectorModel);
                }

                selectorModel.EndpointMetadata.Add(odataMetadata);

                string templateStr = string.IsNullOrEmpty(prefix) ? template : $"{prefix}/{template}";

                selectorModel.AttributeRouteModel = new AttributeRouteModel(new RouteAttribute(templateStr)
                {
                    Name = templateStr
                });

                // Check with .NET Team whether the "Endpoint name metadata"
                selectorModel.EndpointMetadata.Add(new EndpointNameMetadata(Guid.NewGuid().ToString()));
            }
        }
        private static IList <TemplateInfo> AppendFunctionTemplate(IList <TemplateInfo> templates, FunctionSegmentTemplate segment, ODataRouteOptions options)
        {
            Contract.Assert(segment != null);
            Contract.Assert(options != null);

            string qualified  = segment.GetDisplayName(true);
            string unqulified = segment.GetDisplayName(false);

            if (options.EnableQualifiedOperationCall && options.EnableUnqualifiedOperationCall)
            {
                return(CombinateTemplates(templates, (segment.Literal, qualified), (segment.UnqualifiedIdentifier, unqulified)));
            }
            else if (options.EnableQualifiedOperationCall)
            {
                return(CombinateTemplate(templates, (segment.Literal, qualified)));
            }
            else if (options.EnableUnqualifiedOperationCall)
            {
                return(CombinateTemplate(templates, (segment.UnqualifiedIdentifier, unqulified)));
            }
            else
            {
                throw new ODataException(Error.Format(SRResources.RouteOptionDisabledOperationSegment, "function"));
            }
        }