【问题标题】:UpdateModel in MVC creates duplicate/orphaned records of child listMVC 中的 UpdateModel 创建子列表的重复/孤立记录
【发布时间】:2016-01-17 03:20:45
【问题描述】:

我首先使用 MVC 5 和实体框架代码。

我有一个附有学生列表的班级(学校班级)对象,我正在尝试更新班级和/或班级中的学生以及当我在上下文中调用 SaveChanges() 时得到的内容是班级学生的重复记录。基本上,旧的学生列表在数据库中被“孤立”了,而一组全新的学生将附加到数据库中已编辑的班级。

因此,我现在有 20 个学生记录,而不是数据库中的 10 个更正学生记录。 10 个原始(未更正)和 10 个新(已更正)。 10 个原始的已删除 classid 外键,因此它们不再属于该类。

对 Class 对象的任何更新都可以很好地实现,而不会复制 Class 记录。

我看到一个答案表明,可能上下文不知道学生不是新项目,所以它添加了它们......并从数据库中获取学生以便上下文知道他们 - 但是如果当你从数据库中拉出类对象时,学生会来,这和直接拉出不一样吗?

  public class Class
  {
    [Key]
    public Guid ClassId { get; set; }
    [Display(Name="Class Name")]
    public string ClassName { get; set; }

    public virtual List<Student> Students { get; set; }
    public virtual Teacher Teacher { get; set; }

    public Class()
    {
        ClassId = Guid.NewGuid();
        Students = new List<Student>();
    }

   }

    [HttpPost]
    public ActionResult Edit(Guid id, FormCollection collection)
    {                   
        Class selectedclass = db.Classes.Find(id);

        try
        {                                                
            UpdateModel(selectedclass, collection.ToValueProvider());
            db.SaveChanges();
            return RedirectToAction("Details", new { id = id });
        }
        catch
        {
            return View(selectedclass);
        }
    }

我到底做错了什么?

我唯一能想到的就是在我保存更改之前删除数据库中附加到该班级的所有学生记录,但必须有比这更好的方法。

db.Students.RemoveRange(db.Classes.Find(id).Students);

所以我尝试将列表中的学生附加到上下文中,甚至将他们的状态更改为已修改,例如:

 selectedclass.Students.ForEach(s => db.Students.Attach(s));
 selectedclass.Students.ForEach(s => db.Entry(s).State = EntityState.Modified);

但没有运气,仍然会出现重复和孤儿。

尝试直接从数据库中获取学生,但没有成功:

 var ids = selectedclass.Students.Select(s => s.StudentId).ToList();           

 selectedclass.Students = db.Students.Where(s => ids.Contains(s.StudentId)).ToList()

学生编辑器的 HTML:

  @for (int i = 0; i < Model.Students.Count; i++)
  {
      <div class="col-md-8">
          @Html.EditorFor(m => m.Students[i], new { htmlAttributes = new { @class = "form-control" }})
      </div>
  }

学生编辑器模板:

<div class="form-group">
@Html.LabelFor(m => m.FirstName, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-xs-6 col-md-4">
    @Html.EditorFor(m => m.FirstName, new { htmlAttributes = new { @class = "form-control" }})
</div>
@Html.LabelFor(m => m.LastName, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-xs-6 col-md-4">
    @Html.EditorFor(m => m.LastName, new { htmlAttributes = new { @class = "form-control" }})
</div>
</div>

<div class="form-group">
@Html.LabelFor(m => m.UserName, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-xs-6 col-md-4">
    @Html.EditorFor(m => m.UserName, new { htmlAttributes = new { @class = "form-control" } })
</div>
@Html.LabelFor(m => m.Password, htmlAttributes: new { @class = "control-label col-md-2" })
<div class="col-xs-6 col-md-4">
    @Html.EditorFor(m => m.Password, new { htmlAttributes = new { @class = "form-control" }})
</div>
</div>

【问题讨论】:

  • 在使用UpdateModel()前后检查selectedclass的值
  • @StephenMuecke 他们从原来的样子变成了我希望他们更新的样子……没有问题。只是新列表像一个全新的列表一样添加到数据库中,而不是更新现有的学生列表。
  • 但是您在问题中指出 ClassId 属性已被删除?
  • @StephenMuecke - 我的解释不好......被清除的属性在学生对象上,有效地将学生从课堂上移除。它是在数据库中的现有学生上完成的,而不是我正在使用的班级所附列表中的学生。
  • 问题可能出在您的数据库代码上 - 但您没有提供详细信息。可以假设实体框架,然后您可能会遇到分离实体问题,但由于您没有提供任何细节,这只是一个疯狂的猜测。

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


【解决方案1】:

我认为正在发生的事情是您正在使用Class selectedclass = db.Classes.Find(id); 加载实体,但这不会自动加载学生集合。

我怀疑正在发生的事情是因为Students 集合没有加载,很可能UpdateModel 只是调用Students 设置器,它替换集合。

由于上下文没有加载Student 对象,Entity Framework 认为它们是新学生,并相应地插入它们。见Data Points - Why Does Entity Framework Reinsert Existing Objects into My Database?

对此有几个解决方案。您可以在调用 UpdateModel 之前尝试显式加载学生集合。或者,您可以通过将学生记录显式附加到上下文来显式告知实体框架学生记录存在。

但是,总的来说,我尽量避免处理断开连接的实体 - 实体框架断开连接的实体处理中有很多“陷阱”。

另外,在直接绑定到实体框架映射的类时要小心,因为这很容易导致过度发布/发布不足的安全漏洞。见ASP.NET MVC – Think Before You Bind

更新 我开始怀疑问题在于 html 如何指定表单字段。见Model Binding To A List

当然,您的另一个选择是以这种方式使用UpdateModel。相反,只需加载实体并“手动”将编辑应用到加载的实体。从安全角度来看,这不太可能导致问题。在这种情况下,我可能会建议迁移到强类型视图,这样您就不必使用字典键搜索表单集合。

【讨论】:

  • 当我单步执行代码时,一旦加载了 selectedClass,selectedClass 上的学生就会出现。我已经尝试明确加载学生 - 似乎没有用。附加它们,更改它们的状态 - 似乎没有用。
  • 您是否启用了延迟加载?如果是这样,仅仅在调试器中观察变量可能会触发延迟加载,因此您在调试中看到的不一定是在调试之外运行时发生的情况——这取决于 UpdateModel 的确切内部工作方式。虽然由于您还显式加载了学生集合,但我开始认为问题在于您的 html 未根据默认模型绑定器约定呈现 id 字段。
  • @BarryFranklin - 我在您的编辑器模板中没有看到您实际渲染StudentId 的任何地方。它至少需要作为隐藏字段出现,例如@Html.HiddenFor(m =&gt; m.StudentId)
  • 这可能是问题的一部分...我这样做了,然后我也停止使用更新模型方法,只是更新了数据并在上下文中调用了 SaveChanges() 现在工作正常- 数据得到更新,我没有得到重复或孤儿。谢谢。
猜你喜欢
  • 2023-04-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多