1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
|
namespace System.Web.ModelBinding {
using System;
using System.Collections.Generic;
using System.Linq;
public sealed class ModelValidationNode {
public ModelValidationNode(ModelMetadata modelMetadata, string modelStateKey)
: this(modelMetadata, modelStateKey, null) {
}
public ModelValidationNode(ModelMetadata modelMetadata, string modelStateKey, IEnumerable<ModelValidationNode> childNodes) {
if (modelMetadata == null) {
throw new ArgumentNullException("modelMetadata");
}
if (modelStateKey == null) {
throw new ArgumentNullException("modelStateKey");
}
ModelMetadata = modelMetadata;
ModelStateKey = modelStateKey;
ChildNodes = (childNodes != null) ? childNodes.ToList() : new List<ModelValidationNode>();
}
public ICollection<ModelValidationNode> ChildNodes {
get;
private set;
}
public ModelMetadata ModelMetadata {
get;
private set;
}
public string ModelStateKey {
get;
private set;
}
public bool ValidateAllProperties {
get;
set;
}
public bool SuppressValidation {
get;
set;
}
public event EventHandler<ModelValidatedEventArgs> Validated;
public event EventHandler<ModelValidatingEventArgs> Validating;
public void CombineWith(ModelValidationNode otherNode) {
if (otherNode != null && !otherNode.SuppressValidation) {
Validated += otherNode.Validated;
Validating += otherNode.Validating;
foreach (ModelValidationNode childNode in otherNode.ChildNodes) {
ChildNodes.Add(childNode);
}
}
}
private void OnValidated(ModelValidatedEventArgs e) {
EventHandler<ModelValidatedEventArgs> handler = Validated;
if (handler != null) {
handler(this, e);
}
}
private void OnValidating(ModelValidatingEventArgs e) {
EventHandler<ModelValidatingEventArgs> handler = Validating;
if (handler != null) {
handler(this, e);
}
}
private object TryConvertContainerToMetadataType(ModelValidationNode parentNode) {
if (parentNode != null) {
object containerInstance = parentNode.ModelMetadata.Model;
if (containerInstance != null) {
Type expectedContainerType = ModelMetadata.ContainerType;
if (expectedContainerType != null) {
if (expectedContainerType.IsInstanceOfType(containerInstance)) {
return containerInstance;
}
}
}
}
return null;
}
public void Validate(ModelBindingExecutionContext modelBindingExecutionContext) {
Validate(modelBindingExecutionContext, null /* parentNode */);
}
public void Validate(ModelBindingExecutionContext modelBindingExecutionContext, ModelValidationNode parentNode) {
if (modelBindingExecutionContext == null) {
throw new ArgumentNullException("modelBindingExecutionContext");
}
if (SuppressValidation) {
// no-op
return;
}
// pre-validation steps
ModelValidatingEventArgs eValidating = new ModelValidatingEventArgs(modelBindingExecutionContext, parentNode);
OnValidating(eValidating);
if (eValidating.Cancel) {
return;
}
ValidateChildren(modelBindingExecutionContext);
ValidateThis(modelBindingExecutionContext, parentNode);
// post-validation steps
ModelValidatedEventArgs eValidated = new ModelValidatedEventArgs(modelBindingExecutionContext, parentNode);
OnValidated(eValidated);
}
private void ValidateChildren(ModelBindingExecutionContext modelBindingExecutionContext) {
foreach (ModelValidationNode child in ChildNodes) {
child.Validate(modelBindingExecutionContext, this);
}
if (ValidateAllProperties) {
ValidateProperties(modelBindingExecutionContext);
}
}
private void ValidateProperties(ModelBindingExecutionContext modelBindingExecutionContext) {
// Based off CompositeModelValidator.
ModelStateDictionary modelState = modelBindingExecutionContext.ModelState;
// DevDiv Bugs #227802 - Caching problem in ModelMetadata requires us to manually regenerate
// the ModelMetadata.
object model = ModelMetadata.Model;
ModelMetadata updatedMetadata = ModelMetadataProviders.Current.GetMetadataForType(() => model, ModelMetadata.ModelType);
foreach (ModelMetadata propertyMetadata in updatedMetadata.Properties) {
// Only want to add errors to ModelState if something doesn't already exist for the property node,
// else we could end up with duplicate or irrelevant error messages.
string propertyKeyRoot = ModelBinderUtil.CreatePropertyModelName(ModelStateKey, propertyMetadata.PropertyName);
if (modelState.IsValidField(propertyKeyRoot)) {
foreach (ModelValidator propertyValidator in propertyMetadata.GetValidators(modelBindingExecutionContext)) {
foreach (ModelValidationResult propertyResult in propertyValidator.Validate(model)) {
string thisErrorKey = ModelBinderUtil.CreatePropertyModelName(propertyKeyRoot, propertyResult.MemberName);
modelState.AddModelError(thisErrorKey, propertyResult.Message);
}
}
}
}
}
private void ValidateThis(ModelBindingExecutionContext modelBindingExecutionContext, ModelValidationNode parentNode) {
ModelStateDictionary modelState = modelBindingExecutionContext.ModelState;
if (!modelState.IsValidField(ModelStateKey)) {
return; // short-circuit
}
object container = TryConvertContainerToMetadataType(parentNode);
foreach (ModelValidator validator in ModelMetadata.GetValidators(modelBindingExecutionContext)) {
foreach (ModelValidationResult validationResult in validator.Validate(container)) {
string trueModelStateKey = ModelBinderUtil.CreatePropertyModelName(ModelStateKey, validationResult.MemberName);
modelState.AddModelError(trueModelStateKey, validationResult.Message);
}
}
}
}
}
|