【问题标题】:FluentValidation Improperly Validating Model from DropDownFluentValidation 从 DropDown 不正确地验证模型
【发布时间】:2016-05-06 22:46:09
【问题描述】:

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

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

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 似乎吐出了一个错误,因为我的“名称”字段为空。这正是我创建/编辑部门时应该做的事情,但是对于教职员工的下拉列表,它不应该验证整个部门,不是吗?正如我所指定的,下拉列表唯一发回的就是 ID。

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

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

【问题讨论】:

  • 您应该为下拉列表使用带有属性int SelectedDepartment 的视图模型,而不是绑定到复杂对象。您的下拉列表不会回发 Department.Name 的值,因此 ModelState 将始终无效。
  • 感谢您的快速回复。我需要创建一个新类来表示这个 ViewModel,对吗?在其中,我只需输入我需要的所有数据,包括常规教员字段和选定的部门 ID/列表?我将如何处理动态数量的部门?我的 ViewModel 是否只有一个类似于我的常规模型的 ICollection,然后我遍历它们并在数据回发时对每个模型执行 Find(id)?
  • 您是要为FacultMember 选择多个Department(即使用ListBox 或CheckedListBox),还是使用DropDownList 为FacultyMember 选择一个Department
  • 我希望每位教员都属于一个或多个系,具体取决于所讨论的教员是全职还是兼职(我在 FacultyMember 模型中拥有的枚举属性)。所以,我的服务器端验证会检查教员是否是全职的。如果是这样,他们只能有一个部门。否则,他们至少可以拥有一个。我可能会使用某种具有多选属性的列表框,然后在服务器端验证所选项目的数量。
  • 在这种情况下,您的视图模型将需要 2 个“选择”属性 - int SelectedDepartmentint[] SelectedDepartmentsIEnumerable&lt;SelectListItem&gt; DepartmentList` 用于选项。然后根据您的enum 属性的值,您将显示一个下拉列表或一个列表框来选择一个(如果是全职)或一个或多个(如果不是全职)。您可以根据枚举的值应用RequiredIf 验证。

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


【解决方案1】:

正如 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>

...

控制器\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");
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多