ModelStateにエラーを追加するモデルクラスの検証メソッドを追加することになりました。次に、カスタムの ModelValidator と AssociatedValidatorProvider を作成して追加し、フォーム バインディング中に行われる通常の検証中に呼び出すようにしました。こうすることで、Model クラスに直接バインドしないコントローラー アクションでも、モデルの .Validate(ModelState) メソッドを呼び出して検証を偽装できます。このアプローチは、サーバー側のみの検証に適しています。
UserInfoモデル クラス:
private IEnumerable<RuleViolation> GetRuleViolations()
{
List<RuleViolation> violationList = new List<RuleViolation>();
if (String.IsNullOrWhiteSpace(FirstName))
violationList.Add(new RuleViolation("First Name is required.", FirstName"));
return violationList;
}
public void Validate(System.Web.Mvc.ModelStateDictionary ModelState)
{
foreach (RuleViolation violation in GetRuleViolations())
{
ModelState.AddModelError(violation.PropertyName, violation.ErrorMessage);
}
}
これは、コントローラー アクションから直接使用する方法です。このアクションでは、Model クラス オブジェクトが UserSearch モデルの一部として返されます。
public ActionResult Search(UserSearch model)
{
if (this.ModelState.IsValid)
{
model.Search();
if (model.UserInfo != null )
{
model.UserInfo.Validate(ModelState);
}
}...
これが、私が取り組んでいた特定のユース ケースで実行しなければならなかったすべてです。しかし、先に進んで、ポストバックで「通常の」検証を行う作業を完了しました。単純な ModelValidator を作成し、Validate オーバーライドを次のようにしました。すべての Model クラスで上記のパターンに従えば、おそらくこれを再利用することもできます。
public override IEnumerable<ModelValidationResult> Validate(object container)
{
var results = new List<ModelValidationResult>();
if (Metadata.Model != null)
{
UserInfoViewModel uinfo = Metadata.Model as UserInfoViewModel;
foreach (var violation in uinfo.GetRuleViolations())
{
results.Add(new ModelValidationResult
{
MemberName = violation.PropertyName,
Message = violation.ErrorMessage
});
}
}
return results;
}
最後に、AssociatedValidationProvider を拡張してこの ModelValidator を返し、それを Application_Start の ModelValidationProviders コレクションに追加します。これについては、 http: //dotnetslackers.com/articles/aspnet/Customizing-ASP-NET-MVC-2-Metadata-and-Validation.aspx#s2-validation に記事があります。