【问题标题】:Asp.net MVC ModelState.Isvalid returning false for IdAsp.net MVC ModelState.Isvalid 为 Id 返回 false
【发布时间】:2018-02-16 22:22:12
【问题描述】:

我正在看这个ASP.NET MVC course。我有一个 customer model 具有以下属性。

public class Customer {
    public int Id { get; set; }
    [Required]
    [StringLength(255)]        
    public string Name { get; set; }

    [Display(Name = "Date of Birth")]
    public DateTime? DateOfBirth { get; set; }

    public bool IsSubscribedToNewsLetter { get; set; }


    public MembershipType MembershipType { get; set; }

    [Display(Name="Membership Type")]
    public byte? MembershipTypeId { get; set; }
}

请注意,Id 没有 Required 数据注释。但在我的数据库中,Id 是主键,Identity 是该列的真值。

有一个ViewModelCustomerMembershipType 模型组成。

    public class CustomerFormViewModel {
        public IEnumerable<MembershipType> MembershipTypes { get; set; }
        public Customer Customer { get; set; }
    }

我有一个使用NameDateOfBirthMembershipTypeIsSubscribedToNewsLetter 字段创建new Customer 的视图。它需要CustomerFormViewModel

@using Vidly.Models
@model Vidly.ViewModel.CustomerFormViewModel
@{
    ViewBag.Title = "New";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h2>@ViewBag.Message</h2>
@using (Html.BeginForm("Save", "Customer")) {
    <div class="form-group">
        @Html.LabelFor(m => m.Customer.Name,new{@class="control-label"})
        @Html.TextBoxFor(m => m.Customer.Name, new { @class = "form-control" })
        @Html.ValidationMessageFor(m=>m.Customer.Name)
    </div>
    <div class="form-group">
        @Html.LabelFor(m => m.Customer.DateOfBirth, new { @class = "control-label"})
        @Html.TextBoxFor(m => m.Customer.DateOfBirth,"{0:d MMM yyyy}", new { @class = "form-control" })
    </div>

    <div class="form-group">
        @Html.LabelFor(m => m.Customer.MembershipTypeId, new { @class = "control-label"})
        @Html.DropDownListFor(m => m.Customer.MembershipTypeId,new SelectList(Model.MembershipTypes,"Id","Name"), "Select Membership Type", new { @class = "form-control" })
    
    </div>
    <div class="checkbox">
        <label>
            @Html.CheckBoxFor(m => m.Customer.IsSubscribedToNewsLetter) Subscribed To Newsletter?
        </label>

    </div>

    @Html.HiddenFor(m=>m.Customer.Id)
    <button type="submit" class="btn btn-primary">Save</button>
}

这是我的Save 控制器:

    public ActionResult Save(Customer customer) {
        if (!ModelState.IsValid) {
            var viewModel = new CustomerFormViewModel {
                Customer = customer,
                MembershipTypes = _context.MembershipTypes.ToList()
            };
            return View("CustomerForm", viewModel);
        }
        if (customer.Id == 0 || customer.Id==null) {
            _context.Customers.Add(customer);
        }
        else {
            var CustomerInDb = _context.Customers.Single(c => c.Id == customer.Id);
            CustomerInDb.Name = customer.Name;
            CustomerInDb.DateOfBirth = customer.DateOfBirth;
            CustomerInDb.IsSubscribedToNewsLetter = customer.IsSubscribedToNewsLetter;
            CustomerInDb.MembershipTypeId = customer.MembershipTypeId;
        }
        _context.SaveChanges();
        return RedirectToAction("Index", "Customer");
    }

当我填充CustomerForm 视图并单击提交按钮时,ModelState.Isvalid() 方法总是返回 false;导致Save 方法的第一个if statement 为真。所以我不能存储任何新客户。

我尝试通过将breakpoint 放在if (!ModelState.IsValid) 上来debug 应用程序,并看到Id 字段正在创建错误(说“需要ID 字段”)。为什么说Id 不是必需的? ModelState.IsValid 方法是否在数据库级别检查模型?我不这么认为。

如果我像这样更改Customer 模型的Id 属性:public int? Id { get; set; } 并以此更改 if 语句,if ((!ModelState.IsValid) &amp;&amp; (customer.Id==null) ) 应用程序可以正常工作。

这个Id问题还有其他解决方案吗?

【问题讨论】:

  • 必需的。它的 intint 不能是 null。但是视图模型不包含数据模型,尤其是用于编辑数据。仅包含视图中需要的数据模型的属性。参考What is ViewModel in MVC?

标签: asp.net-mvc model modelstate


【解决方案1】:

我观看了相同的课程,我猜作者在您观看后更新了,因为他演示了这种确切类型的问题。问题是,当在新操作上将视图模型返回到视图时,客户属性未初始化,因此 Id 为空,因此尝试保存时 ModelState 失败

只需如下更改,以便在设置 viewModel 时,初始化 Customer 并且 Id 为 0:

 public ActionResult New()
    {
        var memberShipTypes = _context.MembershipTypes.ToList();

        var viewModel = new CustomerViewModel
        {
            Customer = new Customer(),
            MembershipTypes = memberShipTypes
        };

        return View("CustomerForm", viewModel);
    }

【讨论】:

  • 非常感谢 :)
【解决方案2】:

在这方面花了很多时间,并创建了一个解决方法,但似乎 Mosh 在稍后设置电影表单的部分中解决了这个问题。

https://codewithmosh.com/courses/222293/lectures/3684111

简短的回答是将@Html.Hidden("Movie.Id", (Model.Movie != null) ? Model.Movie.Id : 0) 添加到 MovieForm.cshtml。

然后他描述了一种避免将“Movie.Id”硬编码到视图中的方法(参见https://github.com/mosh-hamedani/vidly-mvc-5/commit/e5b994581931a079ad87418ddcf9338e808bd821#diff-e94a8dc96403203b00e58238bb80101c

【讨论】:

    【解决方案3】:

    这只是一个草稿,因为我现在无法访问 VS。无论如何,修改您的保存操作,如下所示:

    public ActionResult Save(CustomerFormViewModel customerVM) {
        if (!ModelState.IsValid) {
            return View(customerVM);
        }
        if (customer.Id == 0) {
            _context.Customers.Add(customerVM.Customer);
        }
        else {
            var CustomerInDb = _context.Customers.Find(customerVM.Customer.Id);
            CustomerInDb.Name = customer.Name;
            CustomerInDb.DateOfBirth = customer.DateOfBirth;
            CustomerInDb.IsSubscribedToNewsLetter = customer.IsSubscribedToNewsLetter;
            CustomerInDb.MembershipTypeId = customer.MembershipTypeId;
        }
        _context.SaveChanges();
        return RedirectToAction("Index", "Customer");
    }
    

    哦,您可以从视图中删除以下内容,因为这是用于创建页面:

        @Html.HiddenFor(m=>m.Customer.Id)
    

    【讨论】:

    • 我不能使用CustomerFormViewModel 有几个原因。即使我这样做了,我也非常怀疑它能否解决我的问题。
    • 它应该可以工作。我已经使用 ASP.NET MVC 一段时间了,这通常对我有用(当然还有一些调试)。无论如何,您已经找到了解决问题的方法,只需很少的代码更改对您非常有用。
    【解决方案4】:

    嘿,在模型类中将 Id 更改为 CustomerId。 我认为只有“Id”可以被视为该模型类的主键。

    【讨论】:

    • 我是故意这样做的,因为我正在使用 code first 迁移与该模型类..
    【解决方案5】:

    我们正在做同样的课程,我遇到了完全相同的问题哈哈。 在我看来,我找到了一个很好的解决方法。

    只需在CustomerFormView 中添加这些代码行。 代替 @Html.HiddenFor(m=&gt;m.Customer.Id)

    添加:

     {
            if (Model.Customer == null)
            {
                <input data-val="true" data-val-number="The field Id must be a number." data-val-required="The Id field is required." id="Customer_Id" name="Customer.Id" type="hidden" value="0" />
            }
            else
            {
                @Html.HiddenFor(m => m.Customer.Id)
            }
        }
    

    由于某种原因,我看到当我尝试添加新客户时,id 的值是一个空字符串而不是零。 因此我手动将其更改为零以防客户对象为空 (添加新客户时总是如此。) 对我来说效果很好。

    如果您认为此解决方案有问题,请告诉我。

    顺便说一句,关于您的问题:“Id 字段正在创建错误(说“Id 字段是必需的”)。为什么说 Id 不是必需的?

    Int 数据类型不可为空,因此它是隐式必需的。与 MembershipId 相同(字节数据类型,没有 [Required] 注释。)

    【讨论】:

      【解决方案6】:

      我也在学习这门课程。我有同样的问题。在 customerviewform 中添加这一行

      if (Model.Customers !=null)
          {
              @Html.HiddenFor(m => m.Customers.Id)
          }
      

      为什么我要添加,因为如果隐藏它用于编辑目的,如果你删除它也会有问题。所以添加这一行我们会工作并且在客户模型中添加公共 int?会员类型。如果您在下拉列表中遇到错误,则在添加新客户时还有另一件事,然后在验证区域返回之前添加此行

       customer.MembershipTypes = _Context.MembershipTypeTableset.ToList(); add this line before     View("CustomerForm", viewModel)
      

      【讨论】:

        【解决方案7】:

        此示例取自 Mosh Hamedani 的 MVC 5 课程。他在第 55 章中解释了 Customer Id 问题。这可以通过在创建 CustomerFormViewModel 时在 New 方法中传递一个新的 customer() 对象来解决。

        【讨论】:

          【解决方案8】:

          也许问题在于Id 字段被标记为int 而不是int?。将变量设置为 int 模型会自动假定该属性将有一个值,因为它没有标记为 nullable

          尝试将Id 属性标记为int?,看看结果是否符合您的预期。

          【讨论】:

          • 我已经尝试过您的建议并将其包含在我的问题中。我要求任何不同的解决方案。也许您没有完全阅读问题。
          • 嗨@AhashanAlamSojib,我完全理解您的要求,但请注意,将Id 作为Db 中的身份并使其在模型中可以为空是不合逻辑的。如果我要制定解决方案,我会在添加新客户时默认保留Id = 0,然后一旦我有了新客户,我就会获得 ID(由 DAO 提供)并将其保存在我的模型中。在_context.SaveChanges()之后可以做customer.Id = CustomerInDb.Id
          【解决方案9】:

          看到this 的问题后,我解决了我的问题。我刚刚在Save 操作的最开始禁用了ModelState 中的Id 错误。

          public ActionResult Save(Customer customer) {
              ModelState["customer.Id"].Errors.Clear();
              if ((!ModelState.IsValid) ) {
                  var viewModel = new CustomerFormViewModel {
                      Customer = customer,
                      MembershipTypes = _context.MembershipTypes.ToList()
                  };
                  return View("CustomerForm", viewModel);
              }
              if (customer.Id == 0) {
                  _context.Customers.Add(customer);
              }
              else {
                  var CustomerInDb = _context.Customers.Single(c => c.Id == customer.Id);
                  CustomerInDb.Name = customer.Name;
                  CustomerInDb.DateOfBirth = customer.DateOfBirth;
                  CustomerInDb.IsSubscribedToNewsLetter = customer.IsSubscribedToNewsLetter;
                  CustomerInDb.MembershipTypeId = customer.MembershipTypeId;
              }
              _context.SaveChanges();
              return RedirectToAction("Index", "Customer");
          }
          

          现在我的应用程序工作正常。

          【讨论】:

          • 在我看来,这并不理想。这是处理问题的一种快速而肮脏的方式。
          • @corix010 我使用脏编码是因为我知道Id 字段不会出现任何其他错误。我很确定。所以砍根似乎是合法的。
          猜你喜欢
          • 2011-02-22
          • 1970-01-01
          • 2013-09-08
          • 1970-01-01
          • 2013-02-26
          • 2019-09-11
          • 2012-11-02
          • 2019-05-20
          • 1970-01-01
          相关资源
          最近更新 更多