使用asp.net-mvc2和实体框架编辑和更新复杂viewmodel对象的正确方法

时间:2022-10-22 11:19:56

I have a table in my database with a one to many relationship to another table, which has a relationship to a third table:

我的数据库中有一个表与另一个表的一对多关系,它与第三个表有关系:

ParentObject

  • ID
  • Name
  • Description

ChildObject

  • ID
  • Name
  • Description
  • ParentObjectID
  • AnotherObjectID

AnotherObject

  • ID
  • Name

The objects are mapped into Entity Framework and exposed through a data access class.

对象映射到实体框架并通过数据访问类公开。

It seemed like ViewModels are recommended when the data to be displayed greatly differs from the domain object, so I created a ViewModel as follows:

当要显示的数据与域对象有很大不同时,建议使用ViewModel,因此我创建了一个ViewModel,如下所示:

public class ViewModel {
    public IList<ParentObject> ParentObjects { get; set; }
    public ParentObject selectedObject { get; set; }
    public IList<ChildObject> ChildObjects { get; set; }
}

I have a view that displays a list of ParentObjects and when clicked will allow a ChildObject to be modified saved.

我有一个显示ParentObjects列表的视图,当单击时将允许保存ChildObject。

<% using (Html.BeginForm()) { %> 
<table>
    <% foreach (var parent in Model.ParentObjects) { %>
    <tr>
        <td>
            ObjectID [<%= Html.Encode(parent.ID)%>]
        </td>
        <td>
            <%= Html.Encode(parent.Name)%>
        </td>
        <td>
            <%= Html.Encode(parent.Description)%>
        </td>
    </tr>
    <% } %>
</table>
<% if (Model.ParentObject != null) { %>
<div>
    Name:<br />
    <%= Html.TextBoxFor(model => model.ParentObject.Name) %>
    <%= Html.ValidationMessageFor(model => model.ParentObject.Name, "*")%>
</div>
<div>
    Description:<br />
    <%= Html.TextBoxFor(model => model.ParentObject.Description) %>
    <%= Html.ValidationMessageFor(model => model.ParentObject.Description, "*")%>
</div>
<div>
    Child Objects
</div>
<% for (int i = 0; i < Model.ParentObject.ChildObjects.Count(); i++) { %>
    <div>
        <%= Html.DisplayTextFor(sd => sd.ChildObjects[i].Name) %>
    </div>
    <div>
        <%= Html.HiddenFor(sd => sd.ChildObjects[i].ID )%>
        <%= Html.TextBoxFor( sd => sd.ChildObjects[i].Description) %>
        <%= Html.ValidationMessageFor(sd => sd.ChildObjects[i].Description, "*") %>
    </div>
    <% }
}
} %>  

This all works fine. My question is around the best way to update the EF objects and persist the changes back to the database. I initially tried:

一切正常。我的问题是更新EF对象并将更改保留回数据库的最佳方法。我最初尝试过:

[HttpPost]
    public ActionResult Edit(ViewModel viewModel) {
        ParentObject parent = myRepository.GetParentObjectByID(viewModel.SelectedObject.ID);

        if ((!ModelState.IsValid)
            || !TryUpdateModel(parent, "SelectedObject", new[] { "Name", "Description" })) {
            || !TryUpdateModel(parent.ChildObjects, "ChildObjects", new[] { "Name", "Description" })) {


            //Code to handle failure and return the current model snipped

            return View(viewModel);
        }

        myRepository.Save();

        return RedirectToAction("Edit");
    }

When I try to save a change to the child object, I get this exception: Entities in 'MyEntities.ChildObject' participate in the 'FK_ChildObject_AnotherObject' relationship. 0 related 'AnotherObject' were found. 1 'AnotherObject' is expected.

当我尝试将更改保存到子对象时,我得到此异常:“MyEntities.ChildObject”中的实体参与“FK_ChildObject_AnotherObject”关系。找到0个相关的'AnotherObject'。 1'AnotherObject'是预期的。

Investigation on * and general googling led me to this blog post that seems to describe my problem: TryUpdateModel() does not correctly handle nested collections. Apparently, (and stepping through the debugger confirms this) it creates a new ChildObject instead of associating with the EF objects from my instantiated context.

调查*和一般的谷歌搜索引导我看到这篇博文,似乎描述了我的问题:TryUpdateModel()没有正确处理嵌套集合。显然,(并通过调试器逐步确认)它创建了一个新的ChildObject,而不是与我实例化的上下文中的EF对象相关联。

My hacky work around is this:

我的hacky工作是这样的:

if (viewModel.ChildObjects.Count > 0) {
    foreach (ChildObject modelChildObject in viewModel.ChildObjects) {
        ChildObject childToUpdate = ParentObject.ChildObject.Where(a => a.ID == modelChildObject.ID).First();
        childToUpdate.Name = modelChildObject.Name;
    }
}

This seems to work fine. My question to you good folks: Is there a correct way to do this? I tried following the suggestion for making a custom model binder per the blog link I posted above but it didn't work (there was an issue with reflection, the code expected certain properties to exist) and I needed to get something going ASAP.

这似乎工作正常。我向你问好的人:有没有正确的方法来做到这一点?我尝试按照上面发布的博客链接制作自定义模型绑定器的建议,但它不起作用(反射存在问题,代码期望某些属性存在),我需要尽快得到一些东西。

PS - I tried to cleanup the code to hide specific information, so beware I may have hosed something up. I mainly just want to know if other people have solved this problem.

PS - 我试图清理代码以隐藏特定信息,所以要小心我可能已经有些东西了。我主要想知道其他人是否已经解决了这个问题。

2 个解决方案

#1


1  

Just briefly looking at the error mentioning FK_ChildObject_AnotherObject... are you sure everything is wired up correctly in your EF data model concerning AnotherObject?

只是简单地看一下提到FK_ChildObject_AnotherObject的错误...你确定在关于AnotherObject的EF数据模型中所有内容都正确连接了吗?

In your question you only list two tables, but this error is indicating that the ChildObject is participating in a 1 to * relationship with AnotherObject and there is not one present in the entity on save causing an error.

在您的问题中,您只列出了两个表,但是此错误表明ChildObject正在与AnotherObject建立1到*的关系,并且在保存时实体中没有一个存在导致错误。

Try removing AnotherObject from the situation in order to test any assumed problems between ParentObject and ChildObject, or try changing the FK_ChildObject_AnotherObject relationship to 0..1 to * briefly for testing.

尝试从情境中删除AnotherObject以测试ParentObject和ChildObject之间的任何假设问题,或者尝试将FK_ChildObject_AnotherObject关系简单地更改为0..1到*以进行测试。

#2


0  

You could look at using Omu ValueInjector instead of tryupdate. It can be configured to be much smarter although I prefer to use it by convention.

你可以看看使用Omu ValueInjector而不是tryupdate。它可以配置得更聪明,虽然我更喜欢按惯例使用它。

tbh not entirely clear on the original question here. I'm not sure why the childobjects in the viewmodel are being detached form the parent in the first place?

这里原始问题并不完全清楚。我不确定为什么viewmodel中的childobjects首先从父类中分离出来?

#1


1  

Just briefly looking at the error mentioning FK_ChildObject_AnotherObject... are you sure everything is wired up correctly in your EF data model concerning AnotherObject?

只是简单地看一下提到FK_ChildObject_AnotherObject的错误...你确定在关于AnotherObject的EF数据模型中所有内容都正确连接了吗?

In your question you only list two tables, but this error is indicating that the ChildObject is participating in a 1 to * relationship with AnotherObject and there is not one present in the entity on save causing an error.

在您的问题中,您只列出了两个表,但是此错误表明ChildObject正在与AnotherObject建立1到*的关系,并且在保存时实体中没有一个存在导致错误。

Try removing AnotherObject from the situation in order to test any assumed problems between ParentObject and ChildObject, or try changing the FK_ChildObject_AnotherObject relationship to 0..1 to * briefly for testing.

尝试从情境中删除AnotherObject以测试ParentObject和ChildObject之间的任何假设问题,或者尝试将FK_ChildObject_AnotherObject关系简单地更改为0..1到*以进行测试。

#2


0  

You could look at using Omu ValueInjector instead of tryupdate. It can be configured to be much smarter although I prefer to use it by convention.

你可以看看使用Omu ValueInjector而不是tryupdate。它可以配置得更聪明,虽然我更喜欢按惯例使用它。

tbh not entirely clear on the original question here. I'm not sure why the childobjects in the viewmodel are being detached form the parent in the first place?

这里原始问题并不完全清楚。我不确定为什么viewmodel中的childobjects首先从父类中分离出来?