我有一个页面,我将两个视图模型发布到控制器,查询和约会。约会在查询中嵌套。用户可以选择在不创建约会的情况下向我们提交查询。
我在视图模型属性上使用内置的MVC必需属性。
我的问题是,当用户选择在没有约会的情况下创建查询时,如何优雅地忽略嵌套的约会视图模型上的验证器并让ModelState.IsValid返回true?
if(!viewModel.CreateAppointment)
{
//ignore the nested view models validation
}
答案 0 :(得分:5)
您可以创建一个自定义IgnoreModelError属性,如下所示,并在视图中使用2个单独的按钮,仅用于查询,另一个用于约会。
// C#
public class IgnoreModelErrorAttribute : ActionFilterAttribute
{
private string keysString;
public IgnoreModelErrorsAttribute(string keys)
: base()
{
this.keysString = keys;
}
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
ModelStateDictionary modelState = filterContext.Controller.ViewData.ModelState;
string[] keyPatterns = keysString.Split(new char[] { ',' },
StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < keyPatterns.Length; i++)
{
string keyPattern = keyPatterns[i]
.Trim()
.Replace(@".", @"\.")
.Replace(@"[", @"\[")
.Replace(@"]", @"\]")
.Replace(@"\[\]", @"\[[0-9]+\]")
.Replace(@"*", @"[A-Za-z0-9]+");
IEnumerable<string> matchingKeys = _
modelState.Keys.Where(x => Regex.IsMatch(x, keyPattern));
foreach (string matchingKey in matchingKeys)
modelState[matchingKey].Errors.Clear();
}
}
}
[HttpPost]
[IgnoreModelErrors("Enquiry.Appointment")]
public ActionResult CreateEnquiryOnly(Enquiry enquiry)
{
// Code for enquiry only.
}
[HttpPost]
public ActionResult CreateAppointment(Enquiry enquiry)
{
// Code for appointment.
}
答案 1 :(得分:2)
嗯,在使用标准数据属性时,没有办法“优雅地”忽略错误。
你有几个选择。快速而肮脏(即不优雅)的方法是清除控制器中ModelState的相关错误。
if (some condition) {
ModelState["controlName"].Errors.Clear();
}
您还可以编写自己的使用条件测试的自定义数据属性。像这里描述的东西:
http://blogs.msdn.com/b/simonince/archive/2011/02/04/conditional-validation-in-asp-net-mvc-3.aspx
第三种方法是避开属性并使用验证框架,例如FluentValidation
最后一个选项是使用JavaScript来确定正确的数据状态,然后修改表单操作URL以发布到不同的Action方法。然后,您可以使用Bind属性修饰操作方法参数,以排除您不想要的数据项。但是,我不推荐这个,因为它要求客户端参与服务器端验证过程。
答案 2 :(得分:2)
这就是我最终做的事情。
这使我可以清除嵌套的Appointments ViewModel上的所有错误。
if (!viewModel.CreateAppointment)
{
foreach (var modelError in ModelState)
{
string propertyName = modelError.Key;
if (propertyName.Contains("AppointmentsViewModel"))
{
ModelState[propertyName].Errors.Clear();
}
}
}
答案 3 :(得分:0)
另一个选项是代替嵌套查询内部查询为您的页面分别创建包含约会和查询模型的ViewModel,然后您可以使用属性包含或排除的Bind属性来选择性地选择要绑定或排除的模型如下图所示。
Public Class EnquiryViewModel
{
public Appointment App {get; set;}
public Enquiry Enq {get; set; }
}
[HttpPost]
//Only bind Enquiry model and it's errors.
public ActionResult CreateEnquiryOnly([Bind(Include = "Enq")]EnquiryViewModel enquiry)
{
if(ModelState.IsValid)
{
// Code for enquiry only.
}
}