Username: ag54348 Password: ******************** using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNet.Mvc.Core; using Microsoft.AspNet.Mvc.Logging; using Microsoft.AspNet.Mvc.Routing; using Microsoft.AspNet.Routing; using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.Logging; namespace Microsoft.AspNet.Mvc { public class DefaultActionSelector : IActionSelector { private readonly IActionDescriptorsCollectionProvider _actionDescriptorsCollectionProvider; private readonly IActionSelectorDecisionTreeProvider _decisionTreeProvider; private readonly INestedProviderManager _actionConstraintProvider; private ILogger _logger; public DefaultActionSelector( [NotNull] IActionDescriptorsCollectionProvider actionDescriptorsCollectionProvider, [NotNull] IActionSelectorDecisionTreeProvider decisionTreeProvider, [NotNull] INestedProviderManager actionConstraintProvider, [NotNull] ILoggerFactory loggerFactory) { _actionDescriptorsCollectionProvider = actionDescriptorsCollectionProvider; _decisionTreeProvider = decisionTreeProvider; _actionConstraintProvider = actionConstraintProvider; _logger = loggerFactory.Create(); } public Task SelectAsync([NotNull] RouteContext context) { using (_logger.BeginScope("DefaultActionSelector.SelectAsync")) { var tree = _decisionTreeProvider.DecisionTree; var matchingRouteConstraints = tree.Select(context.RouteData.Values); var candidates = new List(); foreach (var action in matchingRouteConstraints) { var constraints = GetConstraints(action); candidates.Add(new ActionSelectorCandidate(action, constraints)); } var matchingActionConstraints = EvaluateActionConstraints(context, candidates, startingOrder: null); List matchingActions = null; if (matchingActionConstraints != null) { matchingActions = new List(matchingActionConstraints.Count); foreach (var candidate in matchingActionConstraints) { matchingActions.Add(candidate.Action); } } var finalMatches = SelectBestActions(matchingActions); if (finalMatches == null || finalMatches.Count == 0) { if (_logger.IsEnabled(LogLevel.Verbose)) { _logger.WriteValues(new DefaultActionSelectorSelectAsyncValues() { ActionsMatchingRouteConstraints = matchingRouteConstraints, ActionsMatchingActionConstraints = matchingActions, FinalMatches = finalMatches, }); } return Task.FromResult(null); } else if (finalMatches.Count == 1) { var selectedAction = finalMatches[0]; if (_logger.IsEnabled(LogLevel.Verbose)) { _logger.WriteValues(new DefaultActionSelectorSelectAsyncValues() { ActionsMatchingRouteConstraints = matchingRouteConstraints, ActionsMatchingActionConstraints = matchingActions, FinalMatches = finalMatches, SelectedAction = selectedAction }); } return Task.FromResult(selectedAction); } else { if (_logger.IsEnabled(LogLevel.Verbose)) { _logger.WriteValues(new DefaultActionSelectorSelectAsyncValues() { ActionsMatchingRouteConstraints = matchingRouteConstraints, ActionsMatchingActionConstraints = matchingActions, FinalMatches = finalMatches, }); } var actionNames = string.Join( Environment.NewLine, finalMatches.Select(a => a.DisplayName)); var message = Resources.FormatDefaultActionSelector_AmbiguousActions( Environment.NewLine, actionNames); throw new AmbiguousActionException(message); } } } /// /// Returns the set of best matching actions. /// /// The set of actions that satisfy all constraints. /// A list of the best matching actions. protected virtual IReadOnlyList SelectBestActions(IReadOnlyList actions) { return actions; } private IReadOnlyList EvaluateActionConstraints( RouteContext context, IReadOnlyList candidates, int? startingOrder) { // Find the next group of constraints to process. This will be the lowest value of // order that is higher than startingOrder. int? order = null; foreach (var candidate in candidates) { if (candidate.Constraints != null) { foreach (var constraint in candidate.Constraints) { if ((startingOrder == null || constraint.Order > startingOrder) && (order == null || constraint.Order < order)) { order = constraint.Order; } } } } // If we don't find a 'next' then there's nothing left to do. if (order == null) { return candidates; } // Since we have a constraint to process, bisect the set of actions into those with and without a // constraint for the 'current order'. var actionsWithConstraint = new List(); var actionsWithoutConstraint = new List(); var constraintContext = new ActionConstraintContext(); constraintContext.Candidates = candidates; constraintContext.RouteContext = context; foreach (var candidate in candidates) { var isMatch = true; var foundMatchingConstraint = false; if (candidate.Constraints != null) { constraintContext.CurrentCandidate = candidate; foreach (var constraint in candidate.Constraints) { if (constraint.Order == order) { foundMatchingConstraint = true; if (!constraint.Accept(constraintContext)) { isMatch = false; break; } } } } if (isMatch && foundMatchingConstraint) { actionsWithConstraint.Add(candidate); } else if (isMatch) { actionsWithoutConstraint.Add(candidate); } } // If we have matches with constraints, those are 'better' so try to keep processing those if (actionsWithConstraint.Count > 0) { var matches = EvaluateActionConstraints(context, actionsWithConstraint, order); if (matches?.Count > 0) { return matches; } } // If the set of matches with constraints can't work, then process the set without constraints. if (actionsWithoutConstraint.Count == 0) { return null; } else { return EvaluateActionConstraints(context, actionsWithoutConstraint, order); } } // This method attempts to ensure that the route that's about to generate a link will generate a link // to an existing action. This method is called by a route (through MvcApplication) prior to generating // any link - this gives WebFX a chance to 'veto' the values provided by a route. // // This method does not take httpmethod or dynamic action constraints into account. public virtual bool HasValidAction([NotNull] VirtualPathContext context) { if (context.ProvidedValues == null) { // We need the route's values to be able to double check our work. return false; } var tree = _decisionTreeProvider.DecisionTree; var matchingRouteConstraints = tree.Select(context.ProvidedValues); return matchingRouteConstraints.Count > 0; } private IReadOnlyList GetActions() { var descriptors = _actionDescriptorsCollectionProvider.ActionDescriptors; if (descriptors == null) { throw new InvalidOperationException( Resources.FormatPropertyOfTypeCannotBeNull("ActionDescriptors", _actionDescriptorsCollectionProvider.GetType())); } return descriptors.Items; } private IReadOnlyList GetConstraints(ActionDescriptor action) { if (action.ActionConstraints == null || action.ActionConstraints.Count == 0) { return null; } var items = action.ActionConstraints.Select(c => new ActionConstraintItem(c)).ToList(); var context = new ActionConstraintProviderContext(action, items); _actionConstraintProvider.Invoke(context); return context.Results .Where(item => item.Constraint != null) .Select(item => item.Constraint) .ToList(); } } }