ModelState.IsValid在应该为true时返回false

问题描述:

我有一个非常简单的MVC 2表单。它有两个下拉菜单,用户和角色。无论我选择什么,员工下拉菜单都会通过验证,而角色下拉菜单则不会。虽然我打算实现一个选项,但没有默认的“空白”选项,这就是为什么我需要验证才能工作。它无法同时验证客户端和服务器。我只是不明白为什么一个人会工作,一个人不工作!ModelState.IsValid在应该为true时返回false

的形式为:

<% using (Html.BeginForm()) {%> 

    <%:Html.ValidationSummary(true) %> 
    <%:Html.EditorFor(model => model.User, new { AllEmployees = Model.AllEmployees, RoleList = Model.RoleList })%> 

    <p> 
     <input type="submit" value="Add New User" /> 
    </p> 

    <% } %> 

<% Html.EndForm(); %> 

的编辑模板:

<tr> 
    <td> 
     <div class="editor-label"> 
      <%: Html.LabelFor(model => model.UserId) %> 
      <%: Html.RequiredMarkFor(model => model.UserId) %> 
     </div> 
    </td>  
    <td>  
    <div class="editor-field"> 
     <%: Html.DropDownListFor(model => model.UserId, new SelectList(ViewData["AllEmployees"] as IEnumerable, "UserId", "DisplayName", Model.UserId)) %> 
     <%: Html.ValidationMessageFor(model => model.UserId> 
    </div>  
    </td>  
</tr>  

<tr> 
    <td> 
     <div class="editor-label"> 
      <%: Html.LabelFor(model => model.AccessLevel)%> 
      <%: Html.RequiredMarkFor(model => model.AccessLevel)%> 
     </div> 
    </td>  
    <td>  
     <div class="editor-field"> 
      <%: Html.DropDownListFor(model => model.AccessLevel, new SelectList(ViewData["RoleList"] as IEnumerable, Model.AccessLevel))%> 
      <%: Html.ValidationMessageFor(model => model.AccessLevel)%> 
     </div>  
    </td>  
</tr> 

元数据:

[DisplayName("Employee")] 
    [Required(ErrorMessage = "Please select an employee.")] 
    [StringLength(8, ErrorMessage = "User Id must be less than 8 characters.")] 
    [DisplayFormat(ConvertEmptyStringToNull = false, 
          HtmlEncode = true)] 
    [DataType(DataType.Text)] 
    public object UserId { get; set; } 


    // Validation rules for Access Level 
    [DisplayName("Role")] 
    [Required(ErrorMessage = "Please select the role for this user.")] 
    [StringLength(15, ErrorMessage = "Role must be under 15 characters.")] 
    [DisplayFormat(ConvertEmptyStringToNull = false, 
          HtmlEncode = true)] 
    [DataType(DataType.Text)] 
    public object AccessLevel { get; set; } 

的获取动作:

List<String> roles = (from o in txDB.Users 
             select o.AccessLevel).Distinct().ToList(); 

    var viewModel = new UserViewModel 
    { 
     User = new User(), 
     AllEmployees = empList, 
     RoleList = roles 
    }; 
    return View(viewModel); 

POST操作:

[HttpPost] 
    [AuthorizeAttribute(Roles="Administrator")] 
    public ActionResult Create(User user) 
    { 
     if(!ModelState.IsValid) 
     { 
      //ModelState is invalid 
      return View(new User()); 
     } 
     try 
     { 
      //do stuff 
     } 
    } 

所需的helper方法(从Define markup for [Required] fields in View in ASP.NET MVC 2.0):

public static string RequiredMarkFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Expression<Func<TModel, TProperty>> expression) 
    { 
     if(ModelMetadata.FromLambdaExpression(expression, helper.ViewData).IsRequired) 
      return "*"; 
     else 
      return string.Empty; 
    } 

Post方法应该如下获得服务器端验证...

[HttpPost] 
[AuthorizeAttribute(Roles="Administrator")] 
public ActionResult Create(User user) 
{ 
    if(!TryUpdateModel(user)) 
    { 
     // Model is INVALID 
     return View(user); 
    } 
    else 
    { 
     // ModelState is VALID 
     // Do stuff 
    } 
} 

else可能是多余的取决于你在做什么,但应该让你去。 在上面的观点你<% using Html.BeginForm() %>你需要

<% Html.EnableClientValidation(); %> 

您还需要参考脚本,MicrosoftAjax和MicrosoftMvcValidation我认为

+0

TryUpdateModel如何参照ModelState工作?是的,所有脚本都被引用并添加了客户端验证,只是没有在代码中显示它。 – morganpdx 2010-11-08 19:57:55

+0

好吧,我没有看到代码,所以我不想假设。 TryUpdateModel只是一个我坚持使用的方法,因为有人在这里向我推荐它。 – 2010-11-08 21:25:52

首先:你有两个封闭的形式标记

如果使用

<% using (Html.BeginForm()) {%> 
<% } %> 

你不需要使用这个

<% Html.EndForm(); %> 

关于您正在使用的编辑器只为您的用户属性,这是得到由模型绑定绑定

<%:Html.EditorFor(model => model.User, new { AllEmployees = Model.AllEmployees, RoleList = Model.RoleList })%> 

尝试与EditorForModel来代替以前的代码的唯一一个您的验证问题因为您的编辑器模板适用于模型类。

所以,你的表格应

<% using (Html.BeginForm()) {%> 

    <%:Html.ValidationSummary(true) %> 
    <table> 
     <%:Html.EditorForModel()%> 
    </table> 
    <p> 
     <input type="submit" value="Add New User" /> 
    </p> 
<% } %> 

改变,你就大功告成了!

+0

只使用用户编辑器的目的,因为视图使用的视图模型为了让我通过两个下拉列表。你是否建议我强烈地向用户输入视图,并在viewdata中传递列表? – morganpdx 2010-11-08 19:23:37

+0

您的视图很强类型化到ViewModel。您的EditorTemplate为这两个下拉列表创建html标记。如果你只是使用'Html.EditorFor(model => model.User ...'你告诉视图即使你传递了所有的视图模型,也只是将一个属性绑定到视图。 – Lorenzo 2010-11-08 19:28:18

+0

我试过了 - 效果相同。感谢您指出EndForm的冗余,没有注意到这一点。任何其他想法?我即将注释掉[Required]注释并继续:P – morganpdx 2010-11-08 19:28:53