【问题标题】:ASP.NET MVC Use validation on class propertyASP.NET MVC 对类属性使用验证
【发布时间】:2014-09-23 16:58:19
【问题描述】:

我的 ASP.NET MVC 应用程序中有一个简单的模型,我希望对作为类的属性进行验证。我查看了 ScottGu 的博客 here 关于类级验证,但是当在上下文中调用 SaveChanges() 时会发生验证,这是我不想要的。我希望它的反应就像您在提交表单时使用 DataAnnotations 获得的验证一样。

这是我的模型:

public class SignupModel
{
    [Display(Name = "Company Name")]
    [Required(ErrorMessage = "Company Name Required")]
    public string CompanyName { get; set; }

    [Display(Name = "Company Type")]
    [Required(ErrorMessage = "Company Type Required")]
    public int SelectedCompanyTypeId { get; set; }

    [Display(Name = "Requested Services")]
    [Required(ErrorMessage = "At least 1 service is required")]
    public PostedServicesOffered PostedServices { get; set; }

    public IEnumerable<ServicesOffered> ServicesOffered { get; set; }
    public IEnumerable<ServicesOffered> SelectedServicesOffered { get; set; }
    public IEnumerable<SelectListItem> CompanyTypes { get; set; }
}

PostedServicesOffered 对象是一个具有一个属性的简单类:

public class PostedServicesOffered
{
    public string[] ServiceOfferedIds { get; set; }
}

这是控制器:

    public ActionResult Index()
    {
        SignupModel model = new SignupModel()
        {
            CompanyTypes = SelectCompanyType(),
            ServicesOffered = GetServicesOffered(),
            SelectedServicesOffered = new List<ServicesOffered>(),
            PostedServices = new PostedServicesOffered()
        };

        ViewBag.BannerText = "Sign Up Form";

        return View(model);
    }

    [HttpPost]
    public ActionResult Index(SignupModel model)
    {
        if (ModelState.IsValid)
        {

        }

        return View(GetSignupModel(model));
    }

    private SignupModel GetSignupModel(SignupModel model)
    {
        SignupModel signupModel = new SignupModel();
        var selectedServices = new List<ServicesOffered>();
        var postedServicesOfferedIds = new string[0];

        if (model.PostedServices == null)
            model.PostedServices = new PostedServicesOffered();

        // if a view model array of posted services ids exists
        // and is not empty,save selected ids
        if (model.PostedServices.ServiceOfferedIds != null && model.PostedServices.ServiceOfferedIds.Any())
            postedServicesOfferedIds = model.PostedServices.ServiceOfferedIds;

        //If there are any selected ids saved, create a list of ServicesOffered.
        if (postedServicesOfferedIds.Any())
            selectedServices = GetServicesOffered()
                                .Where(x => postedServicesOfferedIds.Any(s => x.ServicesOfferedId.ToString().Equals(s)))
                                .OrderBy(x => x.ServiceName)
                                .ToList();

        signupModel.CompanyTypes = SelectCompanyType();
        signupModel.ServicesOffered = GetServicesOffered();
        signupModel.SelectedServicesOffered = selectedServices;
        signupModel.PostedServices = model.PostedServices;
        signupModel.SelectedCompanyTypeId = model.SelectedCompanyTypeId;

        return signupModel;
    }

    private IEnumerable<ServicesOffered> GetServicesOffered()
    {
        return Repository.GetServicesOffered(new UnitOfWork()).OrderBy(so => so.ServiceName);
    }

    private IEnumerable<SelectListItem> SelectCompanyType()
    {
        List<SelectListItem> items = new List<SelectListItem>();
        var companyTypes = Repository.GetCompanyTypes(new UnitOfWork()).OrderBy(ct => ct.CompanyType1);

        foreach (CompanyType companyType in companyTypes)
            items.Add(new SelectListItem()
            {
                Text = companyType.CompanyType1,
                Value = companyType.CompanyTypeId.ToString()
            });

        return items;
    }

和视图:

<div id="contact-form" class="contatct-form">
    <h4 class="classic-title"><span>Registration</span></h4>
    @using (Html.BeginForm())
    {
        <div class="validation-text">
            <h5>@Html.ValidationSummary()</h5>
        </div>
        <div class="row padBtm-20">
            <div class="col-md-4">
                @Html.LabelFor(x => x.CompanyName)
                @Html.TextBoxFor(x => x.CompanyName, new { placeholder = "Company Name..." })
            </div>
            <div class="col-md-4">
                @Html.LabelFor(x => x.SelectedCompanyTypeId)
                @Html.DropDownListFor(x => x.SelectedCompanyTypeId, Model.CompanyTypes)
                @Html.HiddenFor(x => x.SelectedCompanyTypeId, new { value = Model.SelectedCompanyTypeId })
            </div>
            <div class="col-md-4">
                @Html.LabelFor(model => model.PostedServices)<br />
                @*@Html.CheckBoxListFor(model => model.PostedServices.ServiceOfferedIds,
                                        model => model.ServicesOffered,
                                        service => service.ServicesOfferedId,
                                        service => service.ServiceName,
                                        model => model.SelectedServicesOffered,
                                        Position.Vertical)*@
                @Html.CheckBoxListFor(model => model.PostedServices,
                                        model => model.ServicesOffered,
                                        service => service.ServicesOfferedId,
                                        service => service.ServiceName,
                                        model => model.SelectedServicesOffered,
                                        Position.Vertical)
            </div>
        </div>
        <div class="row padBtm-20"></div>                        
        <input type="submit" class="button" value="Send Message" />
    }

</div>

简而言之,ServicesOffered 属性是显示为复选框的服务列表。我正在使用一个名为 MvcCheckBoxList 的 NeGut 包(找到 here),它基于列表创建一组复选框。

发生的情况是,当我点击Submit 并选中no 复选框时,表单首先验证其他属性并显示那些错误消息first。只有这些字段通过验证并发布表单后,PostedServices 字段才会通过验证并显示错误。如何在不发布所有其他属性的情况下对其进行验证?

【问题讨论】:

  • 您想对复选框执行什么类型的验证?您也应该能够将数据注释添加到列表中。如果您的验证更复杂,那么您始终可以让模型实现 IValidatableObject
  • 在生成的所有复选框中,至少需要选中1个。我试图让模型从 IValidatableObject 继承,但是当按下提交按钮时,Validate 事件永远不会触发。

标签: asp.net asp.net-mvc validation data-annotations


【解决方案1】:

问题是您将字符串数组封装在另一个类中。让我分解验证时发生的情况,以便您了解为什么这不起作用。

首先,验证发生在客户端和服务器端。您似乎遇到的问题是,除此属性之外的所有其他内容都是在客户端验证的。然后,一旦表单实际发布,此特定属性在服务器端失败并导致表单在该点重新显示。通过将data-* 属性添加到表单字段来进行客户端验证。这些属性反过来通知客户端验证代码运行什么类型的验证。 Required 属性显然使客户端代码强制输入一个值,但问题是您的 PostedServices 属性在您的表单上的任何地方都没有表示。相反,您有一个PostedServices.ServiceOfferedIds 字段,特别是不需要

一旦表单发布,modelbinder 会更新SignupModel 的实例,映射发布的数据。但是,由于没有为 ServiceOfferedIds 发布任何内容,因此它没有任何内容可用于创建 PostedServicesOffered 的实例,因此将 PostedServices 属性设置为 null。然后,Required 验证捕获服务器端。

我不确定您为什么选择将这个属性封装在另一个类中,但是如果您将 PostedServices 更改为:

[Display(Name = "Requested Services")]
[Required(ErrorMessage = "At least 1 service is required")]
public string[] PostedServices { get; set; }

这样您就可以直接与字符串数组进行交互,一切都应该可以正常工作。

【讨论】:

  • 感谢您的深入解释,我现在明白为什么会这样了。我正在关注该网站用于该 NuGet 包的示例(我将其添加到我的问题中),这就是它按原样设置的原因。我尝试了你的建议,但仍然无法验证(我不得不注释掉 GetSignupModel 函数,因为它正在寻找 PostedServices.ServiceOfferedIds 属性。
  • MvcCheckBoxList 是否带有任何 JavaScript?复选框列表的验证将是自定义的,因为您必须将它们视为一个组,即不需要每个单独的复选框(再次为默认客户端验证不附加任何内容)。否则,您必须检查每一项才能通过验证。因此,必须有代码检查整个组,以确保实际检查了至少一个。
  • 我不相信它有任何 JS。我该如何为此创建自定义验证?我最初下载了这个包,因为我想要一种简单的方法来为我的数据库中的对象列表创建复选框。如果以另一种方式更容易获得客户验证,我完全赞成。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-16
相关资源
最近更新 更多