FluentValidation从DropDown中不正确地验证模型

时间:2016-05-06 22:46:09

标签: c# model asp.net-mvc-5 fluentvalidation

我有以下两个模型(剥离相关部分):

模型\ Department.cs:

public class DepartmentValidator : AbstractValidator<Department> {
    public DepartmentValidator() {
        RuleFor(d => d.Name)
            .NotEmpty().WithMessage("You must specify a name.")
            .Length(0, 256).WithMessage("The name cannot exceed 256 characters in length.");
    }
}

[Validator(typeof(DepartmentValidator))]
public class Department {
    public int Id { get; set; }

    [Column(TypeName = "nvarchar")]
    [MaxLength(256)]
    public string Name { get; set; }
}

模型\ FacultyMember.cs:

public class FacultyValidator : AbstractValidator<FacultyMember> {
    public FacultyValidator() {
        RuleFor(f => f.Name)
            .NotEmpty().WithMessage("You must specify a name.")
            .Length(0, 64).WithMessage("The name cannot exceed 64 characters in length.");
    }
}

[Validator(typeof(FacultyValidator))]
public class FacultyMember {
    public int Id { get; set; }

    [Column(TypeName = "nvarchar")]
    [MaxLength(64)]
    public string Name { get; set; }

    public virtual ICollection<Department> Departments { get; set; }

    public FacultyMember() {
        Departments = new HashSet<Department>();
    }
}

我有以下控制器代码:

控制器\ FacultyController.cs:

// GET: Faculty/Create
public ActionResult Create() {
    // Get Departments.
    var departmentList = db.Departments.ToList().Select(department => new SelectListItem {
        Value = department.Id.ToString(),
        Text = department.Name
    }).ToList();

    ViewBag.DepartmentList = departmentList;

    var facultyMember = new FacultyMember();
    facultyMember.Departments.Add(new Department()); // Create a single dropdown for a department to start out.
    return View(facultyMember);
}

// POST: Faculty/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Id,Name,Departments")] FacultyMember facultyMember) {
    // Get Departments.
    var departmentList = db.Departments.ToList().Select(department => new SelectListItem {
        Value = department.Id.ToString(),
        Text = department.Name
    }).ToList();

    ViewBag.DepartmentList = departmentList;

    if (!ModelState.IsValid) { // Problem here...
        return View(facultyMember);
    }

    db.Faculty.Add(facultyMember);
    db.SaveChanges();

    return RedirectToAction("Index");
}

视图\系\ Create.cshtml:

...

<div class="form-group">
    @Html.LabelFor(model => model.Departments, new { @class = "control-label col-md-2" })
    <div class="col-md-10">
        @Html.EditorFor(model => model.Departments, new { htmlAttributes = new { @class = "form-control" } })
        @Html.ValidationMessageFor(model => model.Departments, "", new { @class = "text-danger" })
    </div>
</div>

...

视图\共享\ EditorTemplates \ Department.cshtml:

@model MyProject.Models.Department

@Html.DropDownListFor(model => model.Id, ViewBag.DepartmentList as IEnumerable<SelectListItem>, "Select...", new { @class = "form-control" })

因此,当我导航到创建教师页面时,一切都正常显示; “部门”字段有一个下拉列表,其中包含我数据库中的部门。但是,在提交表单时,我的模型状态无效(请参阅上面的代码中的注释)。经过进一步检查,似乎FluentValidation吐出错误,因为我的“名称”字段为空。这正是我在创建/编辑部门时应该做的事情,但是对于教职员工的这种下拉,它应该不是在验证整个部门,不是吗?下拉列表发回的唯一内容是Id,正如我所指定的那样。

此下拉列表发送的唯一内容是部门的ID,已正确接收。那么,我需要做些什么来使这项工作?我的目标是拥有一组动态下拉列表,每个列表都填充了数据库中的现有部门。与this示例类似。

如果还有其他事情需要解释,请告诉我。

1 个答案:

答案 0 :(得分:0)

正如Stephen Muecke所解释的那样,解决方案是创建一个视图模型来表示我想传递给表单并返回的所有数据。

视图模型\ FacultyMemberViewModel.cs:

public class FacultyMemberViewModelValidator : AbstractValidator<FacultyMemberViewModel> {
    public FacultyMemberViewModelValidator() {
        RuleFor(f => f.Name)
            .NotEmpty().WithMessage("You must specify a name.")
            .Length(0, 64).WithMessage("The name cannot exceed 64 characters in length.");

        RuleFor(s => s.SelectedDepartments)
            .NotEmpty().WithMessage("You must specify at least one department.")
    }
}

[Validator(typeof(FacultyMemberViewModelValidator))]
public class FacultyMemberViewModel {
    public int Id { get; set; }

    public string Name { get; set; }

    public int[] SelectedDepartments { get; set; }
    [DisplayName("Departments")]
    public IEnumerable<SelectListItem> DepartmentList { get; set; }
}

视图\系\ Create.cshtml:

...

<div class="form-group">
    @Html.LabelFor(model => model.DepartmentList, new { @class = "control-label col-md-2" })
    <div class="col-md-10">
        @Html.ListBoxFor(model => model.SelectedDepartments, Model.DepartmentList, new { @class = "form-control" })             @Html.ValidationMessageFor(model => model.SelectedDepartments, "", new { @class = "text-danger" })
    </div>
</div>

...

控制器\ FacultyController.cs:

// GET: Faculty/Create
public ActionResult Create() {
    var facultyMemberViewModel = new FacultyMemberViewModel {
        DepartmentList = GetDepartmentList()
    };

    return View(facultyMemberViewModel);
}

// POST: Faculty/Create
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Create([Bind(Include = "Id,Name,SelectedDepartments,DepartmentList")] FacultyMemberViewModel facultyMemberViewModel) {
    if (!ModelState.IsValid) {
        // Re-set the Department list.
        if (facultyMemberViewModel.DepartmentList == null) {
            facultyMemberViewModel.DepartmentList = GetDepartmentList();
        }

        return View(facultyMemberViewModel);
    }

    var facultyMember = new FacultyMember {
        Id = facultyMemberViewModel.Id,
        Name = facultyMemberViewModel.Name,
    };

    foreach (var departmentId in facultyMemberViewModel.SelectedDepartments) {
        // I'm assuming this is safe to do (aka the records exist in the database)...
        facultyMember.Departments.Add(db.Departments.Find(departmentId));
    }

    db.Faculty.Add(facultyMember);
    db.SaveChanges();

    return RedirectToAction("Index");
}