【问题标题】:ASP.NET MVC Save List<T> to databaseASP.NET MVC 将 List<T> 保存到数据库
【发布时间】:2015-05-26 23:51:30
【问题描述】:

我在将列表项保存到数据库时遇到问题。当要保存字段时(在 HTTP POST Create 中),某些 Lists 属性不必保存,因此我允许 nullabe 用于此类。但是,我从表单中检索并保存了一个字段。由于类很复杂,我将限制我将在此处发布的代码(我将使用一个字段,因为生成的异常是相同的)。

StringValues 是多个 List 字段继承自的 Class,例如本例中的 TestPlanChecklist。

public class TestPlanChecklist:StringValues
{
}

public class StringValues
{
    [Key]
    public int id { get; set; }

    public int ChangeRequestsID { get; set; }
    public string Value { get; set; }

    public ChangeRequests ChangeRequests { get; set; }
}

我的模型类的一部分

    public class ChangeRequests
{

    [Required]
    public List<TestPlanChecklist> TestPlanChecklist { get; set; }

    [Required]
    public List<PostActivityChecklist> PostActivityChecklist { get; set; }

    [Required]
    public List<CMBApproval> CMBApproval { get; set; }

    [Required]
    public List<TechnicalFeasibility> TechnicalFeasibility { get; set; }

}

在我的创建视图中,这是为 TestPlanChecklist 字段呈现文本框的代码

<div class="form-group">
            @Html.LabelFor(model => model.TestPlanChecklist, htmlAttributes: new { @class = "control-label col-md-2" })
            <div class="col-md-10">
                <div>
                    <label class="numbers"> 1 </label>                        
                    <input type="text" class="TestPlanChecklist" name="TestPlan" />

                    <input type="button" value="+" class="roundButton" onclick="add('TestPlanChecklist', 'TestPlan')" />
                    <input type="button" value="-" class="roundButton" onclick="removeElement('TestPlan')" />
                </div>

                <div>
                    <label class="numbers"> 2 </label>
                    <input type="text" class="TestPlanChecklist" name="TestPlan" />
                </div>
                <div>  
                    <label class="numbers"> 3 </label>
                    <input type="text" class="TestPlanChecklist" name="TestPlan" />
                </div>

                @Html.ValidationMessageFor(model => model.TestPlanChecklist, "", new { @class = "text-danger" })
            </div>
        </div>

    </div>

和HttpPost创建方法

[HttpPost]
    [ValidateAntiForgeryToken]
    public ActionResult Create([Bind(Include = "TestPlanChecklist,PostActivityChecklist,PostActivityChecklist,CMBApproval,TechnicalFeasibility")] ChangeRequests changeRequests,
      string[] TestPlan)
    {            
        changeRequests.TestPlanChecklist = new List<TestPlanChecklist>();


        foreach (var test in TestPlan)
            changeRequests.TestPlanChecklist.Add(new TestPlanChecklist { Value = test });

        //SendEmails(TechnicalFeasibility, User.Identity.Name, ChangeUrgency, Priority, DescriptionOfChange, Reason);

        //SendEmails(CMBApproval, User.Identity.Name, ChangeUrgency, Priority, DescriptionOfChange, Reason);


        if (ModelState.IsValid)
        {
            db.ChangeRequests.Add(changeRequests);
            db.SaveChanges();
            return RedirectToAction("List");
        }

        return View(changeRequests);
    }

请注意,我只是使用一个字段来提出这个问题,这就是为什么我删除了用于初始化其他 List 字段的代码。

ModelState.IsValid

返回假。我意识到所有 List 字段都有一个错误,这表明它不可能从 System.String 类型转换到特定类。这很有趣,因为我只分配了从表单中检索到的一个字段值,其余的都是可以为空的,这是有道理的。

我哪里出错了?

提前致谢。

【问题讨论】:

  • 非常不清楚您要在这里做什么。您生成的唯一表单控件具有name 属性“TestPlan”,它与您的模型无关。为什么你的POST方法有参数ChangeRequests changeRequests? - 您不会发布与ChangeRequests 类型的任何属性相关的任何内容,因此由于[Required] 属性,验证将始终失败。您还有一个 LabelFor()ValidationMessageFor() 与属性 TestPlanChecklist 相关联,这是没有意义的(同样,您不会为 TestPlanChecklist 的任何属性生成任何表单控件!)
  • @StephenMuecke 表单控件的名称为 TestPlan,因此在我的后构造函数中,我可以添加一个字符串数组,然后将每个字符串添加到 List 字段中。我只使用ValidationMessageFor()LabelFor(),因为名为TestPlan 的表单控件设置了TestPlanChecklist 的一个属性。我在 HTTP POST 方法上设置了其他字段,其中一些将在应用程序使用期间稍后设置(更新)。
  • 您对如何使用 MVC 的误解。您没有与模型中的任何属性相关的表单控件,因此您的 POST 方法不应具有ChangeRequests 的参数!但是您应该拥有一个 List&lt;string&gt; TestPlan 的视图模型,如果您正在编辑它,则将其发布回来。
  • @StephenMuecke 我在问题中提到我只会显示给我带来麻烦的字段的代码。因为它们是六个这样的字段,所以我使用了 TestPlanChecklist,因为每个字段都抛出了相同的异常。如果我在我的视图中发布了所有代码以及我的模型,那么问题的可读性将受到很大影响
  • 我之前的评论仍然存在“您对如何使用 MVC 的误解” 我已经为您回答了其他问题,这是显而易见的。我强烈建议您访问 MVC 站点并学习一些基本教程。除非您的表单控件使用索引器正确命名,否则您无法保存集合,并且您手动创建输入的尝试将始终无法绑定到集合

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


【解决方案1】:

ModelState.IsValid 检查验证规则,在你的情况下来自DataAnnotations [Required] ChangeRequests 中的属性ViewModel

如果您想使用此验证并使您的某些 List 属性可以为空,您应该删除此属性。

【讨论】:

  • 如何在不使用 [Required] 属性的情况下仍确保该列表字段的至少一条记录保存到数据库中?
【解决方案2】:

您可以检查验证的错误。

var errores = new List<ModelError>();
foreach (ModelState modelState in ViewData.ModelState.Values)
{
    foreach (ModelError error in modelState.Errors)
    {
        errores.Add(error);
    }
}

【讨论】:

  • 正如我所指出的,错误无法从 System.String 类型转换为现在我为 List 字段定义的类。但是,我并没有尝试将我从表单中检索到的值直接保存到列表中,而是将它们全部添加到我的类中的一个字段中,例如 foreach (var test in TestPlan) changeRequests.TestPlanChecklist.Add(new TestPlanChecklist { Value = test }); 但是我仍然收到 ModelState 错误。
【解决方案3】:

您会收到 ModelState 错误,因为 ChangeRequest 具有 [Required] 属性并由 Mvc 验证。我不清楚您为什么要使用该动作签名,但这是一种不好的方法。您应该直接依赖ViewModels 而不是Models

public ActionResult Create(ChangeRequestViewModel viewModel)
{
    if(ModelState.IsValid == false) return View(viewModel); 
    var changeRequest = new ChangeRequest();
    foreach(var testPlan in viewModel.TestPlans) {
         changeRequest.TestPlanChecklist.Add(new TestPlanChecklist { Value = testPlan }
    }
    // ...
} 

【讨论】:

  • Required 属性是我的错误的原因。我曾认为一旦一个属性可以为空,那么我可以只为一个类设置一个属性并且仍然可以侥幸逃脱。虽然这解决了我的问题,但它产生了另一个问题,如何在不使用 [Required] 属性的情况下获得服务器端验证以确保该字段至少保存一个列表项?
  • @Dennis 假设您遵循我的建议并使用视图模型,您应该查看 github.com/JeremySkinner/FluentValidation 进行认真验证
  • 这是我的问题的答案,但是其他人也比您先发布了相同的答案。因此,我已将其标记为答案,但我非常感谢您提供的代码。在我看到您的代码后进行的一些研究指出,viewModels 应该只用于显示数据(因此名称中的“视图”。保存将需要一个数据模型。这只是为了跟上 MVC 的关注点分离。+ 1 为您的答案。
猜你喜欢
  • 2011-08-23
  • 2017-05-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多