Exemplo n.º 1
0
        /// <summary>
        /// Route precedence algorithm.
        /// We collect all the routes and sort them from most specific to
        /// less specific. The specificity of a route is given by the specificity
        /// of its segments and the position of those segments in the route.
        /// * A literal segment is more specific than a parameter segment.
        /// * A parameter segment with more constraints is more specific than one with fewer constraints
        /// * Segment earlier in the route are evaluated before segments later in the route.
        /// For example:
        /// /Literal is more specific than /Parameter
        /// /Route/With/{parameter} is more specific than /{multiple}/With/{parameters}
        /// /Product/{id:int} is more specific than /Product/{id}
        ///
        /// Routes can be ambiguous if:
        /// They are composed of literals and those literals have the same values (case insensitive)
        /// They are composed of a mix of literals and parameters, in the same relative order and the
        /// literals have the same values.
        /// For example:
        /// * /literal and /Literal
        /// /{parameter}/literal and /{something}/literal
        /// /{parameter:constraint}/literal and /{something:constraint}/literal
        ///
        /// To calculate the precedence we sort the list of routes as follows:
        /// * Shorter routes go first.
        /// * A literal wins over a parameter in precedence.
        /// * For literals with different values (case insensitive) we choose the lexical order
        /// * For parameters with different numbers of constraints, the one with more wins
        /// If we get to the end of the comparison routing we've detected an ambiguous pair of routes.
        /// </summary>
        internal static int RouteComparison(MultiTenantRouteEntry x, MultiTenantRouteEntry y)
        {
            if (ReferenceEquals(x, y))
            {
                return(0);
            }

            var xTemplate     = x.Template;
            var yTemplate     = y.Template;
            var minSegments   = Math.Min(xTemplate.Segments.Length, yTemplate.Segments.Length);
            var currentResult = 0;

            for (var i = 0; i < minSegments; i++)
            {
                var xSegment = xTemplate.Segments[i];
                var ySegment = yTemplate.Segments[i];

                var xRank = GetRank(xSegment);
                var yRank = GetRank(ySegment);

                currentResult = xRank.CompareTo(yRank);

                // If they are both literals we can disambiguate
                if ((xRank, yRank) == (0, 0))
                {
                    currentResult = StringComparer.OrdinalIgnoreCase.Compare(xSegment.Value, ySegment.Value);
                }

                if (currentResult != 0)
                {
                    break;
                }
            }

            if (currentResult == 0)
            {
                currentResult = xTemplate.Segments.Length.CompareTo(yTemplate.Segments.Length);
            }

            if (currentResult == 0)
            {
                throw new InvalidOperationException($@"The following routes are ambiguous:
'{x.Template.TemplateText}' in '{x.Handler.FullName}'
'{y.Template.TemplateText}' in '{y.Handler.FullName}'
");
            }

            return(currentResult);
        }
Exemplo n.º 2
0
        internal static MultiTenantRouteTable Create(Dictionary <Type, string[]> templatesByHandler)
        {
            var routes = new List <MultiTenantRouteEntry>();

            foreach (var keyValuePair in templatesByHandler)
            {
                var parsedTemplates        = keyValuePair.Value.Select(v => MultiTenantTemplateParser.ParseTemplate(v)).ToArray();
                var allRouteParameterNames = parsedTemplates
                                             .SelectMany(GetParameterNames)
                                             .Distinct(StringComparer.OrdinalIgnoreCase)
                                             .ToArray();

                foreach (var parsedTemplate in parsedTemplates)
                {
                    var unusedRouteParameterNames = allRouteParameterNames
                                                    .Except(GetParameterNames(parsedTemplate), StringComparer.OrdinalIgnoreCase)
                                                    .ToArray();
                    var entry = new MultiTenantRouteEntry(parsedTemplate, keyValuePair.Key, unusedRouteParameterNames);
                    routes.Add(entry);
                }
            }

            return(new MultiTenantRouteTable(routes.OrderBy(id => id, RoutePrecedence).ToArray()));
        }