我会建议一个我在过去一周左右才尝试过的实验。
基于这个灵感,我正在创建 DTO,其验证与该DataAnnotations
方法的验证略有不同。示例 DTO:
public class Contact : DomainBase, IModelObject
{
public int ID { get; set; }
public string Name { get; set; }
public LazyList<ContactDetail> Details { get; set; }
public DateTime Updated { get; set; }
protected override void ConfigureRules()
{
base.AddRule(new ValidationRule()
{
Properties = new string[] { "name" },
Description = "A Name is required but must not exceed 300 characters in length and some special characters are not allowed",
validator = () => this.Name.IsRequired300LenNoSpecial()
});
base.AddRule(new ValidationRule()
{
Properties = new string[] { "updated" },
Description = "required",
validator = () => this.Updated.IsRequired()
});
}
}
这可能看起来比DataAnnotations
而且还好,这是因为它是,但它并不大。我认为它在课堂上更形象(我现在有一些非常丑陋的带有DataAnnotations
属性的 DTO 类——你甚至再也看不到属性了)。而且这个应用程序中匿名代表的力量几乎是值得一书的(所以我正在发现)。
基类:
public partial class DomainBase : IDataErrorInfo
{
private IList<ValidationRule> _rules = new List<ValidationRule>();
public DomainBase()
{
// populate the _rules collection
this.ConfigureRules();
}
protected virtual void ConfigureRules()
{
// no rules if not overridden
}
protected void AddRule(ValidationRule rule)
{
this._rules.Add(rule);
}
#region IDataErrorInfo Members
public string Error
{
get { return String.Empty; } // Validation should call the indexer so return "" here
} // ..we dont need to support this property.
public string this[string columnName]
{
get
{
// get all the rules that apply to the property being validated
var rulesThatApply = this._rules
.Where(r => r.Properties.Contains(columnName));
// get a list of error messages from the rules
StringBuilder errorMessages = new StringBuilder();
foreach (ValidationRule rule in rulesThatApply)
if (!rule.validator.Invoke()) // if validator returns false then the rule is broken
if (errorMessages.ToString() == String.Empty)
errorMessages.Append(rule.Description);
else
errorMessages.AppendFormat("\r\n{0}", rule.Description);
return errorMessages.ToString();
}
}
#endregion
}
ValidationRule
和我的验证功能:
public class ValidationRule
{
public string[] Properties { get; set; }
public string Description { get; set; }
public Func<bool> validator { get; set; }
}
/// <summary>
/// These extention methods return true if the validation condition is met.
/// </summary>
public static class ValidationFunctions
{
#region IsRequired
public static bool IsRequired(this String str)
{
return !str.IsNullOrTrimEmpty();
}
public static bool IsRequired(this int num)
{
return num != 0;
}
public static bool IsRequired(this long num)
{
return num != 0;
}
public static bool IsRequired(this double num)
{
return num != 0;
}
public static bool IsRequired(this Decimal num)
{
return num != 0;
}
public static bool IsRequired(this DateTime date)
{
return date != DateTime.MinValue;
}
#endregion
#region String Lengths
public static bool IsLengthLessThanOrEqual(this String str, int length)
{
return str.Length <= length;
}
public static bool IsRequiredWithLengthLessThanOrEqual(this String str, int length)
{
return !str.IsNullOrTrimEmpty() && (str.Length <= length);
}
public static bool IsRequired300LenNoSpecial(this String str)
{
return !str.IsNullOrTrimEmpty() &&
str.RegexMatch(@"^[- \r\n\\\.!:*,@$%&""?\(\)\w']{1,300}$",
RegexOptions.Multiline) == str;
}
#endregion
}
如果我的代码看起来很乱,那是因为我最近几天才研究这种验证方法。我需要这个想法来满足一些要求:
- 我需要支持
IDataErrorInfo
接口,以便我的 MVC 层自动验证
- 我需要能够支持复杂的验证场景(我猜你问题的重点):我希望能够针对同一个对象上的多个属性进行验证(即 StartDate 和 FinishDate);来自不同/多个/关联对象的属性,例如我在对象图中的属性;甚至其他我还没有想到的事情。
- 我需要支持将错误应用于多个属性的想法
- 作为我的 TDD 和 DDD 旅程的一部分,我希望我的域对象比我的服务层方法描述更多我的“域”,因此将这些复杂条件放在模型对象(而不是 DTO)中似乎可以实现这一点
我认为这种方法会让我得到我想要的,也许你也一样。
我想如果你和我一起加入这件事,我们会很“靠自己”,但这可能是值得的。我正在阅读MVC 2 中的新验证功能,但如果没有自定义修改,它仍然不符合上述愿望清单。
希望这可以帮助。