void CheckOverloads(IEnumerable<ActionInfo> actions)
        {
            var overloadedActions =
            (from a in actions
             where a.RouteParameters.Count > 0
             group a by new { a.Controller, Name = a.ActionSegment } into g
             where g.Count() > 1
             select g).ToList();

             if (!Provider.CanDisambiguateActionOverloads) {

            var withoutRequiredAttr =
               (from g in overloadedActions
                let distinctParamCount = g.Select(a => a.RouteParameters.Count).Distinct()
                where distinctParamCount.Count() > 1
                let bad = g.Where(a => !a.HasActionOverloadDisambiguationAttribute)
                where bad.Count() > 0
                select bad).ToList();

            if (withoutRequiredAttr.Count > 0) {

               var first = withoutRequiredAttr.First();

               throw new InvalidOperationException(
                  String.Format(CultureInfo.InvariantCulture,
                     "The following action methods must be decorated with {0} for disambiguation: {1}.",
                     Provider.ActionOverloadDisambiguationAttributeType.FullName,
                     String.Join(", ", first.Select(a => String.Concat(a.DeclaringType.FullName, ".", a.MethodName, "(", String.Join(", ", a.Parameters.Select(p => p.Type.Name)), ")")))
                  )
               );
            }
             }

             var overloadsComparer = new ActionSignatureComparer();

             var overloadsWithDifferentParameters =
            (from g in overloadedActions
             let ordered = g.OrderByDescending(a => a.RouteParameters.Count).ToArray()
             let first = ordered.First()
             where !ordered.Skip(1).All(a => overloadsComparer.Equals(first, a))
             select g).ToList();

             if (overloadsWithDifferentParameters.Count > 0) {

            var first = overloadsWithDifferentParameters.First();

            throw new InvalidOperationException(
               String.Format(CultureInfo.InvariantCulture,
                  "Overloaded action methods must have route parameters that are equal in name, position and constraint ({0}).",
                  String.Concat(first.Key.Controller.Type.FullName, ".", first.First().MethodName)
               )
            );
             }
        }
Exemplo n.º 2
0
        static IEnumerable <IEnumerable <ActionInfo> > GroupActions(IEnumerable <ActionInfo> actions)
        {
            var groupedActions =
                (from a in actions
                 let declaringType1 = a.DeclaringType
                                      let declaringType = (declaringType1.IsGenericType) ?
                                                          declaringType1.GetGenericTypeDefinition()
                : declaringType1
                                                          group a by new {
                Depth = a.Controller.CodeRoutingNamespace.Count,
                a.Controller.IsRootController,
                a.Controller.Namespace,
                NamespaceSegments = String.Join("/", a.Controller.NamespaceSegments),
                ControllerCustomRoute = a.Controller.CustomRoute,
                DeclaringType = declaringType,
                a.CustomRoute,
                HasRouteParameters = (a.RouteParameters.Count > 0)
            } into g
                 orderby g.Key.IsRootController descending,
                 g.Key.Depth,
                 g.Key.Namespace,
                 g.Key.HasRouteParameters descending
                 select g
                ).ToList();

            var signatureComparer = new ActionSignatureComparer();
            var finalGrouping     = new List <IEnumerable <ActionInfo> >();

            for (int i = 0; i < groupedActions.Count; i++)
            {
                var set = groupedActions[i];

                if (set.Key.HasRouteParameters)
                {
                    var ordered = set.OrderByDescending(a => a.RouteParameters.Count).ToList();

                    while (ordered.Count > 0)
                    {
                        var firstInSet = ordered.First();
                        var similar    = ordered.Skip(1).Where(a => signatureComparer.Equals(firstInSet, a)).ToList();

                        if (similar.Count > 0)
                        {
                            var signatureCompat = new[] { firstInSet }.Concat(similar).ToArray();

                            var maxParamCounts =
                                (from a in signatureCompat
                                 group a by a.ActionSegment into g
                                 select g.Select(a => a.RouteParameters.Count).Max()
                                ).Distinct().ToArray();

                            foreach (var count in maxParamCounts)
                            {
                                var sameMaxNumberOfParams =
                                    (from a in signatureCompat
                                     group a by a.ActionSegment into g
                                     where g.Select(a => a.RouteParameters.Count).Max() == count
                                     select g)
                                    .SelectMany(g => g)
                                    .Distinct()
                                    .OrderByDescending(a => a.RouteParameters.Count)
                                    .ToArray();

                                var index          = 0;
                                var k              = 0;
                                var overloadRanges =
                                    (from a in sameMaxNumberOfParams
                                     let idx = ++index
                                               let next = sameMaxNumberOfParams.ElementAtOrDefault(idx)
                                                          let diff = (next == null) ? 0 : Math.Abs(a.RouteParameters.Count - next.RouteParameters.Count)
                                                                     let key = (diff == 1 || diff == 0) ?
                                                                               k : k++
                                                                               group a by key into g
                                                                               select g).ToArray();

                                foreach (var range in overloadRanges)
                                {
                                    if (range.Count() > 1)
                                    {
                                        var first = range.First();
                                        var last  = range.Last();

                                        foreach (var param in first.RouteParameters.Skip(last.RouteParameters.Count))
                                        {
                                            param.IsOptional = true;
                                        }
                                    }

                                    finalGrouping.Add(range);

                                    foreach (var item in range)
                                    {
                                        ordered.Remove(item);
                                    }
                                }
                            }
                        }
                        else
                        {
                            finalGrouping.Add(new[] { firstInSet });
                            ordered.Remove(firstInSet);
                        }
                    }
                }
                else
                {
                    finalGrouping.Add(set);
                }
            }

            return(finalGrouping);
        }