[英]ASP.NET MVC - Complex model validation
我有一个像这样的ViewModel类:
class CaseModel {
public Boolean ClientPresent { get; set; }
public ClientModel Client { get; set; }
}
class ClientModel {
[Required]
public String FirstName { get; set; }
[Required]
public String LastName { get; set; }
}
该视图页面由一个<input type="checkbox" name="ClientPresent" />
和一个Html.EditorFor( m => m.Client )
部分视图组成。
想法是,当用户提供有关案例(业务域对象)的信息时,他们可以通过不选中ClientPresent框来选择不指定有关客户端的信息(另一个biz对象)。
我希望ASP.NET MVC不对子ClientModel对象执行任何验证-但是,将表单回发到服务器时会自动填充CaseModel.Client属性,但是(因为)(不一定)提供了FirstName
和LastName
用户,这意味着它未通过[Required]
验证属性,因此ViewData.ModelState.IsValid
返回false,并且用户收到验证错误消息。
我怎样才能得到它这样CaseModel.Client
如果无法通过验证CaseModel.ClientPresent
是假的?
请注意, ClientModel
是一个完全独立的ViewModel类,并且在应用程序的其他位置(例如,允许用户编辑Clients的各个实例的ClientController类)使用。
我意识到我的问题与绑定无关,而实际上与验证有关:通过保留值,这意味着当用户重新加载页面时将填充相同的表单字段,我只需要舍弃验证消息即可,因为它们不是适用。
为此,我意识到我可以执行模型属性验证,但是随后使用一些自定义逻辑来删除验证消息。 这类似于我所做的事情:
public class CaseModel {
public void CleanValidation(ModelStateDictionary dict) {
if( this.ClientPresent ) {
dict.Keys.All( k => if( k.StartsWith("Client") dict[k].Errors.Clear() );
}
}
}
(显然,我的实际代码更健壮,但是您可以理解一般想法)
CleanValidation方法由控制器的action方法直接调用:
public void Edit(Int64 id, CaseModel model) {
model.CleanValidation( this.ModelState );
}
我可以通过将CleanValidation
作为方法添加到新接口IComplexModel
并IComplexModel
具有新的模型绑定程序自动调用此方法来进行IComplexModel
,从而使控制器不需要自身调用它。
我有此接口,该接口适用于任何需要复杂验证的ViewModel:
public interface ICustomValidation {
void Validate(ModelStateDictionary dict);
}
在我的原始示例中, CaseModel
现在看起来像这样:
public class CaseClientModel : ICustomValidation {
public Boolean ClientIsNew { get; set; } // bound to a radio-button
public ClientModel ExistingClient { get; set; } // a complex viewmodel used by a partial view
public ClientModel NewClient { get; set; } // ditto
public void Validate(ModelStateDictionary dict) {
// RemoveElementsWithPrefix is an extension method that removes all key/value pairs from a dictionary if the key has the specified prefix.
if( this.ClientIsNew ) dict.RemoveElementsWithPrefix("ExistingClient");
else dict.RemoveElementsWithPrefix("NewClient");
}
}
验证逻辑由我常见的BaseController
类中的OnActionExecuting
调用:
protected override void OnActionExecuting(ActionExecutingContext filterContext) {
base.OnActionExecuting(filterContext);
if( filterContext.ActionParameters.ContainsKey("model") ) {
Object model = filterContext.ActionParameters["model"];
ModelStateDictionary modelState = filterContext.Controller.ViewData.ModelState; // ViewData.Model always returns null at this point, so do this to get the ModelState.
ICustomValidation modelValidation = model as ICustomValidation;
if( modelValidation != null ) {
modelValidation.Validate( modelState );
}
}
}
您必须通过继承默认模型联编程序来创建自定义模型联编程序。
public class CustomModelBinder: DefaultModelBinder
{
protected override void BindProperty(ControllerContext controllerContext, ModelBindingContext bindingContext, PropertyDescriptor propertyDescriptor)
{
if (propertyDescriptor.Name == "Client")
{
var clientPresent = bindingContext.ValueProvider.GetValue("ClientPresent");
if (clientPresent == null ||
string.IsNullOrEmpty(clientPresent.AttemptedValue))
return;
}
base.BindProperty(controllerContext, bindingContext, propertyDescriptor);
}
}
Global.asax.cs
ModelBinders.Binders.Add(typeof(CaseModel), new CustomModelBinder());
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.