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) ) ); } }
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); }