| File: ModelStateDictionaryExtensions.cs | Web Access |
| Project: src\aspnetcore\src\Mvc\Mvc.ViewFeatures\src\Microsoft.AspNetCore.Mvc.ViewFeatures.csproj (Microsoft.AspNetCore.Mvc.ViewFeatures) |
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. using System.Linq; using System.Linq.Expressions; using Microsoft.AspNetCore.Mvc.ViewFeatures; namespace Microsoft.AspNetCore.Mvc.ModelBinding; /// <summary> /// Extensions methods for <see cref="ModelStateDictionary"/>. /// </summary> public static class ModelStateDictionaryExtensions { /// <summary> /// Adds the specified <paramref name="errorMessage"/> to the <see cref="ModelStateEntry.Errors"/> instance /// that is associated with the specified <paramref name="expression"/>. If the maximum number of allowed /// errors has already been recorded, ensures that a <see cref="TooManyModelErrorsException"/> exception is /// recorded instead. /// </summary> /// <typeparam name="TModel">The type of the model.</typeparam> /// <param name="modelState">The <see cref="ModelStateDictionary"/> instance this method extends.</param> /// <param name="expression">An expression to be evaluated against an item in the current model.</param> /// <param name="errorMessage">The error message to add.</param> public static void AddModelError<TModel>( this ModelStateDictionary modelState, Expression<Func<TModel, object>> expression, string errorMessage) { ArgumentNullException.ThrowIfNull(modelState); ArgumentNullException.ThrowIfNull(expression); ArgumentNullException.ThrowIfNull(errorMessage); modelState.AddModelError(GetExpressionText(expression), errorMessage); } /// <summary> /// Adds the specified <paramref name="exception"/> to the <see cref="ModelStateEntry.Errors"/> instance /// that is associated with the specified <paramref name="expression"/>. If the maximum number of allowed /// errors has already been recorded, ensures that a <see cref="TooManyModelErrorsException"/> exception is /// recorded instead. /// </summary> /// <remarks> /// This method allows adding the <paramref name="exception"/> to the current <see cref="ModelStateDictionary"/> /// when <see cref="ModelMetadata"/> is not available or the exact <paramref name="exception"/> /// must be maintained for later use (even if it is for example a <see cref="FormatException"/>). /// </remarks> /// <typeparam name="TModel">The type of the model.</typeparam> /// <param name="modelState">The <see cref="ModelStateDictionary"/> instance this method extends.</param> /// <param name="expression">An expression to be evaluated against an item in the current model.</param> /// <param name="exception">The <see cref="Exception"/> to add.</param> public static void TryAddModelException<TModel>( this ModelStateDictionary modelState, Expression<Func<TModel, object>> expression, Exception exception) { ArgumentNullException.ThrowIfNull(modelState); ArgumentNullException.ThrowIfNull(expression); modelState.TryAddModelException(GetExpressionText(expression), exception); } /// <summary> /// Adds the specified <paramref name="exception"/> to the <see cref="ModelStateEntry.Errors"/> instance /// that is associated with the specified <paramref name="expression"/>. If the maximum number of allowed /// errors has already been recorded, ensures that a <see cref="TooManyModelErrorsException"/> exception is /// recorded instead. /// </summary> /// <typeparam name="TModel">The type of the model.</typeparam> /// <param name="modelState">The <see cref="ModelStateDictionary"/> instance this method extends.</param> /// <param name="expression">An expression to be evaluated against an item in the current model.</param> /// <param name="exception">The <see cref="Exception"/> to add.</param> /// <param name="metadata">The <see cref="ModelMetadata"/> associated with the model.</param> public static void AddModelError<TModel>( this ModelStateDictionary modelState, Expression<Func<TModel, object>> expression, Exception exception, ModelMetadata metadata) { ArgumentNullException.ThrowIfNull(modelState); ArgumentNullException.ThrowIfNull(expression); ArgumentNullException.ThrowIfNull(metadata); modelState.AddModelError(GetExpressionText(expression), exception, metadata); } /// <summary> /// Removes the specified <paramref name="expression"/> from the <see cref="ModelStateDictionary"/>. /// </summary> /// <typeparam name="TModel">The type of the model.</typeparam> /// <param name="modelState">The <see cref="ModelStateDictionary"/> instance this method extends.</param> /// <param name="expression">An expression to be evaluated against an item in the current model.</param> /// <returns> /// true if the element is successfully removed; otherwise, false. /// This method also returns false if <paramref name="expression"/> was not found in the model-state dictionary. /// </returns> public static bool Remove<TModel>( this ModelStateDictionary modelState, Expression<Func<TModel, object>> expression) { ArgumentNullException.ThrowIfNull(modelState); ArgumentNullException.ThrowIfNull(expression); return modelState.Remove(GetExpressionText(expression)); } /// <summary> /// Removes all the entries for the specified <paramref name="expression"/> from the /// <see cref="ModelStateDictionary"/>. /// </summary> /// <typeparam name="TModel">The type of the model.</typeparam> /// <param name="modelState">The <see cref="ModelStateDictionary"/> instance this method extends.</param> /// <param name="expression">An expression to be evaluated against an item in the current model.</param> public static void RemoveAll<TModel>( this ModelStateDictionary modelState, Expression<Func<TModel, object>> expression) { ArgumentNullException.ThrowIfNull(modelState); ArgumentNullException.ThrowIfNull(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<Func<TModel, object>> // automatically creates a convert to object expression for expressions // returning value types var unaryExpression = expression.Body as UnaryExpression; if (IsConversionToObject(unaryExpression)) { return ExpressionHelper.GetUncachedExpressionText(Expression.Lambda( unaryExpression.Operand, expression.Parameters[0])); } return ExpressionHelper.GetUncachedExpressionText(expression); } private static bool IsConversionToObject(UnaryExpression expression) { return expression?.NodeType == ExpressionType.Convert && expression.Operand?.NodeType == ExpressionType.MemberAccess && expression.Type == typeof(object); } }