FluentValidation从DropDown错误地验证模型



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

型号\部门.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>();
    }
}

我有以下控制器代码:

Controllers\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");
}

Views\Faculty\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>
...

Views\Shared\EditorTemplates\Department.cshtml:

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

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

此下拉列表唯一发送的是部门的Id,该Id已正确接收。那么,我需要做些什么才能让它发挥作用呢?我的目标是拥有一组动态的下拉列表,每个下拉列表都填充了数据库中现有的部门。与此示例类似。

如果还有什么需要解释的,请告诉我。

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

ViewModel\ 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; }
}

Views\Faculty\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>
...

Controllers\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");
}

最新更新