// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; using System.Linq; using System.Linq.Expressions; using Microsoft.AspNetCore.Mvc.ViewFeatures.Internal; namespace Microsoft.AspNetCore.Mvc.ModelBinding { /// /// Extensions methods for . /// public static class ModelStateDictionaryExtensions { /// /// Adds the specified to the instance /// that is associated with the specified . /// /// The type of the model. /// The instance this method extends. /// An expression to be evaluated against an item in the current model. /// The error message to add. public static void AddModelError( this ModelStateDictionary modelState, Expression> expression, string errorMessage) { if (modelState == null) { throw new ArgumentNullException(nameof(modelState)); } if (expression == null) { throw new ArgumentNullException(nameof(expression)); } if (errorMessage == null) { throw new ArgumentNullException(nameof(errorMessage)); } modelState.AddModelError(GetExpressionText(expression), errorMessage); } /// /// Adds the specified to the instance /// that is associated with the specified . /// /// The type of the model. /// The instance this method extends. /// An expression to be evaluated against an item in the current model. /// The to add. /// The associated with the model. public static void AddModelError( this ModelStateDictionary modelState, Expression> expression, Exception exception, ModelMetadata metadata) { if (modelState == null) { throw new ArgumentNullException(nameof(modelState)); } if (expression == null) { throw new ArgumentNullException(nameof(expression)); } if (metadata == null) { throw new ArgumentNullException(nameof(metadata)); } modelState.AddModelError(GetExpressionText(expression), exception, metadata); } /// /// Removes the specified from the . /// /// The type of the model. /// The instance this method extends. /// An expression to be evaluated against an item in the current model. /// /// true if the element is successfully removed; otherwise, false. /// This method also returns false if was not found in the model-state dictionary. /// public static bool Remove( this ModelStateDictionary modelState, Expression> expression) { if (modelState == null) { throw new ArgumentNullException(nameof(modelState)); } if (expression == null) { throw new ArgumentNullException(nameof(expression)); } return modelState.Remove(GetExpressionText(expression)); } /// /// Removes all the entries for the specified from the /// . /// /// The type of the model. /// The instance this method extends. /// An expression to be evaluated against an item in the current model. public static void RemoveAll( this ModelStateDictionary modelState, Expression> expression) { if (modelState == null) { throw new ArgumentNullException(nameof(modelState)); } if (expression == null) { throw new ArgumentNullException(nameof(expression)); } string modelKey = GetExpressionText(expression); if (string.IsNullOrEmpty(modelKey)) { var modelMetadata = new EmptyModelMetadataProvider().GetMetadataForType(typeof(TModel)); for (var i = 0; i < modelMetadata.Properties.Count; i++) { var property = modelMetadata.Properties[i]; var childKey = property.BinderModelName ?? property.PropertyName; var entries = modelState.FindKeysWithPrefix(childKey).ToArray(); foreach (var entry in entries) { modelState.Remove(entry.Key); } } } else { var entries = modelState.FindKeysWithPrefix(modelKey).ToArray(); foreach (var entry in entries) { modelState.Remove(entry.Key); } } } private static string GetExpressionText(LambdaExpression expression) { // We check if expression is wrapped with conversion to object expression // and unwrap it if necessary, because Expression> // automatically creates a convert to object expression for expressions // returning value types var unaryExpression = expression.Body as UnaryExpression; if (IsConversionToObject(unaryExpression)) { return ExpressionHelper.GetExpressionText(Expression.Lambda( unaryExpression.Operand, expression.Parameters[0])); } return ExpressionHelper.GetExpressionText(expression); } private static bool IsConversionToObject(UnaryExpression expression) { return expression?.NodeType == ExpressionType.Convert && expression.Operand?.NodeType == ExpressionType.MemberAccess && expression.Type == typeof(object); } } }