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 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267
|
namespace System.Web.DynamicData {
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.Drawing;
using System.Globalization;
using System.Linq;
using System.Web;
using System.Web.Resources;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.DynamicData.Util;
/// <summary>
/// Validator that enforces model validation. It can be used either at the field level or the entity level
/// </summary>
[ToolboxBitmap(typeof(DynamicValidator), "DynamicValidator.bmp")]
public class DynamicValidator : BaseValidator {
private IDynamicDataSource _dataSource;
private Exception _exception;
private Dictionary<Type, bool> _ignoredModelValidationAttributes;
/// <summary>
/// The name of the column to be validated, or null for entity level validation
/// </summary>
[Browsable(false)]
[Themeable(false)]
public string ColumnName {
get {
return (Column == null) ? String.Empty : Column.Name;
}
}
private IDynamicDataSource DynamicDataSource {
get {
if (_dataSource == null) {
// get data source for the parent container.
_dataSource = this.FindDataSourceControl();
// get the data source for the targeted data bound control.
if (_dataSource == null) {
Control c = NamingContainer.FindControl(ControlToValidate);
if (c == null) {
throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture,
DynamicDataResources.DynamicValidator_ControlNotFound, ControlToValidate, ID));
}
_dataSource = c.FindDataSourceControl();
}
}
return _dataSource;
}
}
/// <summary>
/// The column to be validated, or null for entity level validation
/// </summary>
[Browsable(false)]
[Themeable(false)]
public MetaColumn Column { get; set; }
/// <summary>
/// The validation exception that occurred, if any
/// </summary>
protected virtual Exception ValidationException {
get {
return _exception;
}
set {
_exception = value;
}
}
/// <summary>
/// Overridden from base
/// </summary>
protected override bool ControlPropertiesValid() {
bool hasDataSource = DynamicDataSource != null;
// We can't call the base when there is no Column, because the control will be something like
// a GridView, which doesn't have a validation property and would cause the base to fail.
if (String.IsNullOrEmpty(ColumnName)) {
return hasDataSource;
}
return base.ControlPropertiesValid();
}
/// <summary>
/// Overridden from base
/// </summary>
protected override bool EvaluateIsValid() {
// Check if there were some model exceptions (e.g. OnProductNameChanging throwing)
Exception e = ValidationException;
if (e != null) {
ErrorMessage = HttpUtility.HtmlEncode(e.Message);
return false;
}
if (Column == null)
return true;
string controlValue = GetControlValidationValue(ControlToValidate);
if (controlValue == null) {
// can return null if ControlToValidate is empty, or if the control is not written correctly to return a value
// this does not mean that the value was null
return true;
}
if (Column is MetaForeignKeyColumn || Column is MetaChildrenColumn) {
// do not perform conversion or validation on relationship columns as controlValue is the serialized form
// of a foreign key which would be useless to a validation attribute
return true;
}
// Check if any of our validators want to fail the value
controlValue = (string)Column.ConvertEditedValue(controlValue);
object value;
if (!TryConvertControlValue(controlValue, Column.ColumnType, out value)) {
ErrorMessage = HttpUtility.HtmlEncode(DynamicDataResources.DynamicValidator_CannotConvertValue);
return false;
}
return ValueIsValid(value);
}
internal static bool TryConvertControlValue(string controlValue, Type columnType, out object value) {
try {
if (controlValue == null) {
value = null;
} else if (columnType == typeof(string)) {
value = controlValue;
} else if (controlValue.Length != 0) {
value = Misc.ChangeType(controlValue, columnType);
} else {
value = null;
}
return true;
} catch (Exception) {
value = null;
return false;
}
}
private bool ValueIsValid(object value) {
// Go through all the model validation attribute to make sure they're valid
foreach (var attrib in Column.Attributes.Cast<Attribute>().OfType<ValidationAttribute>()) {
// Ignore it if it's found in the ignore list
if (_ignoredModelValidationAttributes != null &&
_ignoredModelValidationAttributes.ContainsKey(attrib.GetType())) {
continue;
}
//DynamicValidator can not pass in a ValidationContext as it does
//not have an easy way to get the data object row. Hence we will
//not support attributes that require Validation Context (Ex : CompareAttribute).
if (attrib.RequiresValidationContext) {
continue;
}
if (!attrib.IsValid(value)) {
ErrorMessage = HttpUtility.HtmlEncode(StringLocalizerUtil.GetLocalizedString(attrib, Column.DisplayName));
return false;
}
}
return true;
}
internal void SetIgnoredModelValidationAttributes(Dictionary<Type, bool> ignoredModelValidationAttributes) {
_ignoredModelValidationAttributes = ignoredModelValidationAttributes;
}
private void OnException(object sender, DynamicValidatorEventArgs e) {
ValidateException(e.Exception);
}
/// <summary>
/// Overridden from base
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers")]
protected override void OnInit(EventArgs e) {
base.OnInit(e);
// Don't do anything in Design mode
if (DesignMode)
return;
IDynamicDataSource dataSource = DynamicDataSource;
if (dataSource != null) {
// Register for datasource exception so that we're called if an error occurs
// during an update/insert/delete
dataSource.Exception += new EventHandler<DynamicValidatorEventArgs>(OnException);
}
}
/// <summary>
/// Called when an exception happens. Typically, this sets the ValidationException
/// </summary>
/// <param name="exception">The exception</param>
protected virtual void ValidateException(Exception exception) {
if (exception == null) {
return;
}
ValidationException = null;
// IDynamicValidatorExceptions are used by LinqDataSource to wrap exceptions caused by problems
// with setting model properties (columns), such as exceptions thrown from the OnXYZChanging
// methods
IDynamicValidatorException e = exception as IDynamicValidatorException;
if (e != null) {
HandleDynamicValidatorException(e);
} else {
// It's not a column specific exception. e.g. it could be coming from
// OnValidate (DLinq), or could be caused by a database error.
// We only want to use it if it's a ValidationException, otherwise we
// could end up displaying sensitive database errors to the end user
if (Column == null && exception is ValidationException) {
if (exception.InnerException == null) {
ValidationException = exception;
} else {
ValidationException = exception.InnerException;
}
}
}
}
private void HandleDynamicValidatorException(IDynamicValidatorException e) {
if (Column == null) {
// IDynamicValidatorException only applies to column exceptions
return;
}
List<string> columnNames = GetValidationColumnNames(Column);
foreach (string name in columnNames) {
// see if the exception wraps any child exceptions relevant to this column
Exception inner;
if (e.InnerExceptions.TryGetValue(name, out inner)) {
// Stop as soon as we find the first exception.
ValidationException = inner;
return;
}
}
}
/// <summary>
/// Get the names of all the columns that can throw an exception that will affect the setting of the
/// value of the given column.
/// </summary>
private static List<string> GetValidationColumnNames(MetaColumn column) {
List<string> columnNames = new List<string>();
columnNames.Add(column.Name); // add it first so that it gets checked first
var fkColumn = column as MetaForeignKeyColumn;
if (fkColumn != null) {
columnNames.AddRange(fkColumn.ForeignKeyNames);
}
return columnNames;
}
}
}
|