トリッキーな小技なので、本来は使いたくない。
やりたかったこと
検索条件が複数あり、GETクエリで初期表示と検索条件が全て空を分岐したい。
- 初期表示の場合、エラーメッセージを表示しない。
- 検索条件が全て空の場合、エラーメッセージを表示したい。
IClientValidatable
やjavascript側で、検索実行前のValidation処理を実装すれば実現可能だが、簡単な処理に大層な実装をつくりたくない。
ASP.NET MVC 3 で独自の検証属性を作成して、クライアントサイド検証を行う - しばやん雑記 https://blog.shibayan.jp/entry/20110108/1294423577
検索条件が複数あり、検索条件が全て空かの判定は、IValidatableObject
のValidate
メソッドで簡単に実現可能
SearchViewModel.cs
public class SearchViewModel
{
public string KeywordA { get; set; }
public string KeywordB { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var results = new List<ValidationResult>();
if (string.IsNullOrWhiteSpace(KeywordA) && string.IsNullOrWhiteSpace(KeywordB))
{
return results.Add(new ValidationResult("検索条件を入力してください"));
}
return results;
}
}
問題
GETクエリの場合、初期表示と検索条件なしはどちらも検索条件をパラメータを指定していない状態のため、IValidatableObject
のValidate
メソッドで、検索条件に対しstring.IsNullOrWhiteSpace(prop)
を行うと、どちらも同じ結果が返ってしまい、分岐できない。
対応方法
初期表示の場合は、コンストラクタを実行するのみ。
検索条件なしで検索した場合は、ModelBinderを経由するので必ず値はnullになる。
これを利用し、モデルクラス側に空文字""の初期値を設定することで、処理を分岐する。
SearchViewModel.cs
public class SearchViewModel
{
//初期表示の場合は""、検索条件なしで検索した場合はnull
public string KeywordA { get; set; } = "";
public string KeywordB { get; set; } = "";
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
var results = new List<ValidationResult>();
if (string.IsNullOrWhiteSpace(KeywordA) && string.IsNullOrWhiteSpace(KeywordB))
{
return results.Add(new ValidationResult("検索条件を入力してください"));
}
return results;
}
}
HomeController.cs
public class HomeController : Controller
public ActionResult Search(SearchViewModel model)
{
//初期表示の場合はエラーメッセージをクリア
if (model.KeywordA == "" && model.KeywordB == "")
{
ModelState.Clear();
}
else if (ModelState.IsValid)
{
model = repo.Search(model.KeywordA, model.KeywordB);
}
return View(model);
}
}