ModelState.IsValid 首先使用 DTO 和 Entity Framework 代码

ModelState.IsValid with DTO and Entity Framework code first

ASP.NET 4.5,MVC 5,EF6 代码优先

我是新手,可能会问一些问题 long-known 但我无法在网上找到解决方案,可能是因为我不知道正确的术语来表达这个问题。

为了简单起见,假设我有两个模型 类 Teacher 和 Kid;一个孩子只能分配给一位老师,但一位老师可以有很多孩子。由于我首先使用代码,因此我的数据库是根据这些模型构建的 类:

public class Kid
{
    [Required]
    public int Id { get; set; }
    [Required]
    public string Name { get; set; }
    [Required]
    public virtual Teacher { get; set; }    
}

public class Teacher
{
    [Required]
    public int Id { get; set; }
    [Required]
    public string Name { get; set; }
    [Required]
    public virtual ICollection<Kid> Kids { get; set; }
}

现在,我需要一个添加新孩子的视图: 孩子名字的文本框; 包含教师列表的下拉菜单

因此,我正在创建数据传输 object,专门针对该视图:

public class AddNewKidViewDTO
{
    public IEnumerable<SelectListItem> Teachers { get; set; }
    public int SelectedTeacherId { get; set; }
    public Kid Kid { get; set; }
}

我还有一个填充 IEnumerable Teachers 的方法:

public AddNewKidViewDTO LoadTeachersForDropDownList()
{
    ... //get the list of Teachers
    AddNewKidViewDTO addNewKidViewDTO = new AddNewKidViewDTO();
    List<SelectListItem> selectListItems =  new List<SelectListItem>();
    foreach (teacher in Teachers)
    {
        selectListItems.Add (new SelectListItem
        {
            Text = teacher.Name.ToString(),
            Value = teacher.Id.ToString()
        });
    }
    addNewKidViewDTO.Teachers = selectListItems;
    return addNewKidViewDTO;
}

并在视图中 AddNewKid.cshtml

<form>
@Html.LabelFor(model => model.Kid.Name)
@Html.TextBoxFor(model => model.Kid.Name, new {id ="Name"}
<br/>
@Html.LabelFor(model => model.Kid.Teacher)
@Html.DropDownListFor(model => model.SelectedTeacherId, Model.Teachers)
</form>

表单已提交,在控制器中我得到了填充的 AddNewKidViewDTO 模型:

[HttpPost]
public ActionResult SaveNewKid (AddNewKidViewDTO addNewKidViewDTO)
{
    if (ModelState.IsValid)
    {
        //here is where the problem comes
    }
}

ModelState.IsValid 在我的例子中总是 return 错误。 因为当它开始验证 AddNewKidViewDTO.Kid 时,Teacher 是必填字段,但在我的 addNewKidViewDTO 模型中它是空的。我只有 addNewKidViewDTO.SelectedTeacherId 中包含必要的教师 ID。

我的问题是,在传递到我的内部业务逻辑方法之前验证我的模型的优雅方法是什么?

感谢任何帮助。

有多种可能的解决方案:

  1. 更改您的 AddNewKidViewDTO 并使用 DataAnnotaions 装饰它以进行验证:

    public class AddNewKidViewDTO
    {
        public IEnumerable<SelectListItem> Teachers { get; set; }
    
        [Range(1, 2147483647)] //Int32 max value but you may change it
        public int SelectedTeacherId { get; set; }
    
        [Required]
        public string KidName { get; set; }
    }
    

    然后你可以手动创建 Kid 对象,以防你的模型有效。

更新(以解决您的评论)

如果您使用这种方法,您的操作将如下所示:

    [HttpPost]
    public ActionResult SaveNewKid (AddNewKidViewDTO addNewKidViewDTO)
    {
        if (ModelState.IsValid)
        {
            using (var dbContext = new yourContext())
            {
                var teacher = dbContext.Teachers.FirstOrDefault(t=>t.id == addNewKidViewDTO.SelectedTeacherId );
                if(teacher == default(Teacher))
                {
                    //return an error message or add validation error to model state
                } 

                //It is also common pattern to create a factory for models 
                //if you have some logic involved, but in this case I simply
                //want to demonstrate the approach
                var kid = new Kid
                {
                     Name = addNewKidViewDTO.KidName,
                     Teacher = teacher 
                };
                dbContext.SaveChanges();
             }
         }
    }
  1. AddNewKidViewDTO 编写自定义模型联编程序,它将在 Kid 对象中初始化 Teacher 属性,因此一旦您实际使用 Model.IsValid,属性 将是已初始化。