我有客户:
public class Client
{
public int ClientID { get; set; }
public string ClientName { get; set; }
public virtual List<Project> Projects { get; set; }
}
项目:
public class Project
{
public int ProjectID { get; set; }
public string ProjectName { get; set; }
}
客户端控制器有一个详细信息获取操作:
public ActionResult Details(int id)
{
Client client = db.Clients.Find(id);
return View(client);
}
详情发布后:
[HttpPost]
public ActionResult Details(Client client)
{
if (ModelState.IsValid)
{
db.Entry(client).State = EntityState.Modified;
db.SaveChanges();
// reload object from db to populate projects property
client = db.Clients.Find(client.ClientID);
}
return View(client);
}
我的客户详情视图:
@model Client
<h1>@Html.DisplayFor(model => model.ClientName)</h1>
@using (Html.BeginForm()) {
@Html.ValidationSummary(true)
<fieldset>
<legend>Client</legend>
@Html.HiddenFor(model => model.ClientID)
<div class="editor-label">
@Html.LabelFor(model => model.ClientName)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.ClientName)
@Html.ValidationMessageFor(model => model.ClientName)
</div>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<h2>Projects</h2>
<ul>
@foreach (Project project in Model.Projects)
{
<li>@Html.ActionLink(project.ProjectName, "Details", "Project", new { id = project.ProjectID }, null)</li>
}
</ul>
现在,如果我去一个有项目的客户端,它工作正常,我在文本框中看到了客户端名称,以及一个项目列表。我编辑名称并单击“保存”。但随后子类列表上的视图错误迭代,“对象引用未设置为对象的实例。”
我在这一行中特意添加了从db重新加载实例,假设它会重新填充延迟加载列表类,并且我将获得与get请求中相同的实例数据,只需更改名称:
client = db.Clients.Find(client.ClientID);
为什么不在回发中加载子类?
一旦我修复了这个问题,在Client类中添加一个实例化List类的构造函数是不是一个好主意,以防我导航到数据库中没有任何记录的客户端?
答案 0 :(得分:3)
原因是在MVC上构建了一个实际POCO的实例, NOT 是一个派生自POCO的代理类的实例。因此,没有重写的虚拟属性来执行延迟加载。
解决方案是通过条目显式加载集合(替换你的Clients.Find(id)调用):
db.Entry(client).Collection( c => c.Projects ).Load();
答案 1 :(得分:1)
我将不得不考虑ApplyCurrentValues,但目前,这就是我的工作方式:
[HttpPost]
public ActionResult Details(Client client)
{
if (ModelState.IsValid)
{
db.Entry(client).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Details", new { msg = "saved" });
}
client.Projects = db.Clients.Find(client.ClientID).Projects;
return View(client);
}
如果验证失败,则会在传回视图之前重新附加项目。理论上,这应该起作用而不是Action重定向。我也会尝试一下。
答案 2 :(得分:0)
您为重新加载客户端而添加的行将不会执行任何操作,因为上下文已具有客户端的本地副本。您可以通过在进行更新之前急切加载客户端/项目来证明这一点。 (像这样)
client = db.Clients.Find(client.ClientID);
client.Projects.ToList();
db.Entry(client).State = EntityState.Modified;
db.SaveChanges();