xVal 驗證框架預設僅支援單一屬性驗證,無法直接實現「兩次密碼輸入一致」等跨屬性比較驗證。本文展示如何透過類別層級驗證屬性,繼承 ValidationAttribute 並覆寫 IsValid(object value) 取得完整實體實例,實現靈活的比較驗證邏輯。
IsValid(object value) 只能取得單一屬性值,無法存取同實體其他屬性。[AttributeUsage(AttributeTargets.Class)],IsValid 接收整個實體實例,可自由比較任意屬性。[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
public class StringCompareAttribute : ValidationAttribute
{
public string SourceProperty { get; private set; }
public string OriginalProperty { get; private set; }
private const string DefaultErrorMessage = "'{0}' 與 '{1}' 不相等";
public StringCompareAttribute(string sourceProperty, string originalProperty)
: base(DefaultErrorMessage)
{
SourceProperty = sourceProperty;
OriginalProperty = originalProperty;
}
public override string FormatErrorMessage(string name)
{
return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString,
SourceProperty, OriginalProperty);
}
public override bool IsValid(object value) // value 為實體實例
{
var properties = TypeDescriptor.GetProperties(value);
var sourceValue = properties.Find(SourceProperty, true).GetValue(value);
var originalValue = properties.Find(OriginalProperty, true).GetValue(value);
return Object.Equals(sourceValue, originalValue);
}
}
[StringCompare("ConfirmPassword", "Password", ErrorMessage = "兩次密碼輸入不一致")]
public class RegisterViewModel
{
[Required]
[DataType(DataType.Password)]
public string Password { get; set; }
[Required]
[DataType(DataType.Password)]
[Display(Name = "確認密碼")]
public string ConfirmPassword { get; set; }
// 其他屬性...
}
CompareAttribute (MVC 3+ 內建) 支援 Operator (Equal, GreaterThan, LessThan 等) 與 Comparable 介面。PropertyName 支援巢狀屬性路徑(如 Address.City)。IClientValidatable 產生 data-val-compare 等屬性,啟用 jQuery Validate 無縫前端驗證。透過類別層級驗證屬性突破單屬性限制,配合 TypeDescriptor 反射實體屬性,即可在 xVal/MVC 原生驗證管線中優雅實現密碼確認、日期區間、數值比較等跨欄位驗證需求。