【问题标题】:ModelState.IsValid with DTO and Entity Framework code firstModelState.IsValid 与 DTO 和实体框架代码优先
【发布时间】:2015-03-06 10:31:34
【问题描述】:

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

我是一个新手,可能会问一些众所周知的问题,但我在网络上找不到解决方案,可能是因为我不知道提出这个问题的正确术语。

为了简单起见,假设我有两个模型类 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; }
}

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

所以,我正在创建一个数据传输对象,专门用于该视图:

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 将始终返回 false。 因为当它开始验证 AddNewKidViewDTO.Kid 时,Teacher 是必填字段,但在我的 addNewKidViewDTO 模型中它为空。我只有 addNewKidViewDTO.SelectedTeacherId 中包含的必要教师 ID。

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

感谢任何帮助。

【问题讨论】:

  • 在您的控制器中,如果您将addNewKidViewDTO.Kid.Teacher.Id 设置为addNewKidViewDTO.SelectedTeacherId 的值,是否有效?
  • 是的,我有这个想法。但是首先使用 EF 代码,我无法分配 Id。我需要分配整个教师对象。所以它应该像(忽略语法) addNewKidViewDTO.Kid.Teacher = getTecherById(addNewKidDTO.SelectedTeacherId);我试过了,但没用,不知道为什么。

标签: asp.net-mvc entity-framework ef-code-first asp.net-mvc-5 modelstate


【解决方案1】:

有多种可能的解决方案:

  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,该属性就会被初始化。

【讨论】:

  • 谢谢你,@Alex。但我看不出第一种解决方案有什么帮助。在 AddNewKidViewDTO 中,该属性不是字符串,而是 Kid 类型。在实际应用模型中比较复杂,我不能只用字符串 KidName 替换它,很遗憾。将尝试第二个解决方案,自定义模型绑定器。
  • DTO(数据传输对象)用于在应用程序的不同层之间传输数据,因此您可以将其定义为对您有用的任何结构。您可以拥有非常复杂的域模型,但这并不意味着您用于处理此模型的 DTO 也很复杂。我个人试图让 DTO 尽可能简单和扁平。
  • 是的,你是对的。我只是在一夜之间发现我不需要域模型的所有属性......谢谢。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-30
  • 1970-01-01
  • 2014-01-05
  • 1970-01-01
相关资源
最近更新 更多