3

我有客户:

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>

现在,如果我去一个有项目的客户,它工作正常,我在文本框中看到客户名称和项目列表。我编辑名称并单击保存。但随后子类列表迭代的视图错误,“对象引用未设置为对象的实例”。

我在这一行中添加了专门从数据库重新加载实例,假设它会重新填充延迟加载列表类,并且我将拥有与获取请求中相同的实例数据,只是名称更改:

client = db.Clients.Find(client.ClientID);

为什么它没有在回发中加载子类?

一旦我解决了这个问题,在 Client 类中添加一个实例化 List 类的构造函数是否是一个好主意,以防我导航到数据库中没有任何记录的 Client?

4

3 回答 3

4

原因是在 MVC 后构建实际 POCO 的实例,而不是从 POCO 派生的代理类的实例。因此,没有重写的虚拟属性来执行延迟加载。

解决方案是通过条目显式加载集合(以替换您的 Clients.Find(id) 调用):

db.Entry(client).Collection( c => c.Projects ).Load();
于 2013-03-26T23:29:46.723 回答
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 重定向。我也会试试看。

于 2013-03-22T11:45:13.170 回答
0

您添加以重新加载客户端的行不会做任何事情,因为上下文已经具有客户端的本地副本。您可以通过在进行更新之前预先加载客户端/项目来证明这一点。(像这样)

client = db.Clients.Find(client.ClientID);
client.Projects.ToList();
db.Entry(client).State = EntityState.Modified;
db.SaveChanges();
于 2013-03-22T09:17:30.577 回答