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