ASP.NET MVC 3 - EF 4.2 代码优先 - 主从编辑

发布于 2024-12-27 16:35:56 字数 3105 浏览 0 评论 0原文

我被 ASP.NET MVC 3 中的一个问题所困扰 - 从单个 MVC 视图处理主从关系的编辑和更新。

我正在使用 Entity Framework 4.2 代码优先来构建一个小型应用程序。它使用 Person 类(其中包含 NameFirstName 等)和 Address 类(包含街道邮政编码城市等)。

这两个类彼此之间具有 am:n 关系,在 EF 代码优先中建模:

public class Person
{
    private List<Address> _addresses;

    // bunch of scalar properties like ID, name, firstname

    public virtual List<Address> Addresses
    {
        get { return _addresses; }
        set { _addresses = value; }
    }
}

public class Address
{
    private List<Person> _people;

    // bunch of scalar properties like AddressID, street, zip, city

    public virtual List<Person> People
    {
        get { return _people; }
        set { _people = value; }
    }
}

我用一些数据手动填充数据库,并且使用 EF 4.2 检索数据效果很好。

我有一个 ASP.NET MVC PersonController,在其中加载一个 Person(包括其所有当前地址),并使用 Razor 视图显示它。我想允许用户更改或更新现有地址,以及删除或插入地址。控制器从 WCF 服务获取该人的数据,如下所示:

public ActionResult Edit(int id)
{
    Person person = _service.GetPerson(id);
    return View(person);
}

并且在检索时正确填写该人的地址。

该视图工作正常 - 它按预期显示了 Person 实例中的所有数据。

@model DomainModel.Person

<h2>Edit</h2>

@using (Html.BeginForm()) {
    @Html.ValidationSummary(true)
    <fieldset>
        <legend>Person</legend>

        @Html.HiddenFor(model => model.PersonId)

        <div class="editor-label">
            @Html.LabelFor(model => model.Name)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Name)
            @Html.ValidationMessageFor(model => model.Name)
        </div>

        .... and a few more of those scalar properties being edited....  

        <hr/>
        <h4>Addresses</h4>

        @foreach (var item in Model.Addresses)
        {
            <tr>
                <td>
                    @Html.EditorFor(modelItem => item.Street)
                </td>
                <td>
                    @Html.EditorFor(modelItem => item.ZipCode)
                </td>
                <td>
                    @Html.EditorFor(modelItem => item.City)
                </td>
            </tr>
        }
        <hr/>
        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>
}

但是,当我尝试在保存正在编辑的人员时处理 POST 时,我似乎无法访问视图中显示(并且可能已编辑)的地址:

    [HttpPost]
    public ActionResult Edit(Person person)
    {
        if (ModelState.IsValid)
        {
            _service.UpdatePerson(person);
            return RedirectToAction("Index");
        }
        return View(person);
    } 

person.Addresses在这种情况下,属性始终为空。嗯...我错过了什么?如何在 ASP.NET MVC 视图上编辑 Person-to-Addresses 关系,并从以下位置获取 Person AND 其关联的 Addresses我的观点是,所以我可以将它们传递给 EF 以将它们保存回数据库表?

I'm stumped with a problem in ASP.NET MVC 3 - handling editing and updating of a master-detail relationship from a single MVC view.

I'm using Entity Framework 4.2 code-first to build a small app. It uses a Person class (which has Name, FirstName and so on), and an Address class (with Street, Zipcode, City etc.).

These two classes have a m:n relationship to one another, modelled in EF code-first with:

public class Person
{
    private List<Address> _addresses;

    // bunch of scalar properties like ID, name, firstname

    public virtual List<Address> Addresses
    {
        get { return _addresses; }
        set { _addresses = value; }
    }
}

public class Address
{
    private List<Person> _people;

    // bunch of scalar properties like AddressID, street, zip, city

    public virtual List<Person> People
    {
        get { return _people; }
        set { _people = value; }
    }
}

I filled the database manually with some data and retrieval of the data using EF 4.2 works just fine.

I have an ASP.NET MVC PersonController, in which I am loading a Person including all its current addresses, and show it using a Razor view. I'd like to allow the user to change or update existing addresses, as well as delete or insert addresses. The controller gets the data for that person from a WCF service like this:

public ActionResult Edit(int id)
{
    Person person = _service.GetPerson(id);
    return View(person);
}

and the person's addresses are filled properly when retrieving.

The view for this works fine - it shows all the data from the Person instance as expected.

@model DomainModel.Person

<h2>Edit</h2>

@using (Html.BeginForm()) {
    @Html.ValidationSummary(true)
    <fieldset>
        <legend>Person</legend>

        @Html.HiddenFor(model => model.PersonId)

        <div class="editor-label">
            @Html.LabelFor(model => model.Name)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Name)
            @Html.ValidationMessageFor(model => model.Name)
        </div>

        .... and a few more of those scalar properties being edited....  

        <hr/>
        <h4>Addresses</h4>

        @foreach (var item in Model.Addresses)
        {
            <tr>
                <td>
                    @Html.EditorFor(modelItem => item.Street)
                </td>
                <td>
                    @Html.EditorFor(modelItem => item.ZipCode)
                </td>
                <td>
                    @Html.EditorFor(modelItem => item.City)
                </td>
            </tr>
        }
        <hr/>
        <p>
            <input type="submit" value="Save" />
        </p>
    </fieldset>
}

However, when I'm trying to handle the POST upon saving the person being edited, I cannot seem to get access to the addresses that were displayed (and possibly edited) in the view:

    [HttpPost]
    public ActionResult Edit(Person person)
    {
        if (ModelState.IsValid)
        {
            _service.UpdatePerson(person);
            return RedirectToAction("Index");
        }
        return View(person);
    } 

The person.Addresses property is always null in this case. Hmmm.... what am I missing?? How can I edit a Person-to-Addresses relationship on an ASP.NET MVC view, and get back the Person AND its associated Addresses from my view, so I can pass them to EF to save them back to the database tables??

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

高速公鹿 2025-01-03 16:35:56

我怀疑地址的输入字段名称错误。

我建议您使用编辑器模板。因此,将 Razor 视图中的 @foreach 循环替换为对 Addresses 属性的 EditorFor 帮助器的一次调用:

<h4>Addresses</h4>

@Html.EditorFor(x => x.Addresses)

<hr/>

现在为地址定义一个编辑器模板,该模板将自动为集合的每个元素呈现 (~/Views/Shared/EditorTemplates/Address.cshtml):

@model Address
<tr>
    <td>
        @Html.EditorFor(x => x.Street)
    </td>
    <td>
        @Html.EditorFor(x => x.ZipCode)
    </td>
    <td>
        @Html.EditorFor(x => x.City)
    </td>
</tr>

现在,当您提交时,Person 模型上的 Addresses 属性将正确显示边界。

备注:编辑器模板也可以放置在 ~/Views/XXX/EditorTemplates/Address.cshtml 中,其中 XXX 是当前控制器。在这种情况下,它只能在当前控制器的视图中重用,而不是使其全局可见(通过将其放入 Shared 中)。

有关默认模型绑定器的有线格式的更深入概述,您可以查看 以下文章

I suspect that the input fields for the address have wrong names.

I would suggest you using editor templates. So replace your @foreach loop in the Razor view with a single call of the EditorFor helper for the Addresses property:

<h4>Addresses</h4>

@Html.EditorFor(x => x.Addresses)

<hr/>

and now define an editor template for the address which will automatically be rendered for each element of the collection (~/Views/Shared/EditorTemplates/Address.cshtml):

@model Address
<tr>
    <td>
        @Html.EditorFor(x => x.Street)
    </td>
    <td>
        @Html.EditorFor(x => x.ZipCode)
    </td>
    <td>
        @Html.EditorFor(x => x.City)
    </td>
</tr>

Now when you submit, the Addresses property on your Person model will be correctly bound.

Remark: the editor template could also be placed inside ~/Views/XXX/EditorTemplates/Address.cshtml where XXX is the current controller. In this case it can be reused only within the views of the current controller instead of making it globally visible (by putting it in Shared).

For more in-depth overview of how the wire format of the default model binder you may take a look at the following article.

听风吹 2025-01-03 16:35:56

我相信通过将 foreach 块更改为 for 块,您将获得所需的效果,如下所示:

    @for (var i = 0; i < Model.Addresses.Count(); i++)
    {
        <tr>
            <td>
                @Html.EditorFor(model => model.Addresses[i].Street)
            </td>
            <td>
                @Html.EditorFor(model => model.Addresses[i].ZipCode)
            </td>
            <td>
                @Html.EditorFor(model => model.Addresses[i].City)
            </td>
        </tr>
    }

这当然要求可以通过索引访问 Addresses 集合。

这样做的原因是,在 foreach 块中,生成的 html 将类似于:

<input type="text" name="Street" value="Test Street 1" />

for 块将生成类似以下内容的内容:

<input type="text" name="Addresses[0].Street" value="Test Street 1" />

然后模型绑定器使用表单字段名称将其与模型类的属性进行匹配。

这两个示例都经过简化以表达要点,但并非 100% 正确。

I believe you'll get the desired effect by changing the foreach block to a for block as follows:

    @for (var i = 0; i < Model.Addresses.Count(); i++)
    {
        <tr>
            <td>
                @Html.EditorFor(model => model.Addresses[i].Street)
            </td>
            <td>
                @Html.EditorFor(model => model.Addresses[i].ZipCode)
            </td>
            <td>
                @Html.EditorFor(model => model.Addresses[i].City)
            </td>
        </tr>
    }

This of course require that the Addresses collection is accessible by index.

The reason for this is that in the foreach block the resulting html will be something like:

<input type="text" name="Street" value="Test Street 1" />

The for block will instead generate something like:

<input type="text" name="Addresses[0].Street" value="Test Street 1" />

The model binder then uses the form field name to match it against the properties of the model class.

Both examples are simplified to get the point across and aren't 100% correct.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文